xref: /freebsd/sys/dev/hyperv/netvsc/if_hn.c (revision 7029da5c36f2d3cf6bb6c81bf551229f416399e8)
115516c77SSepherosa Ziehau /*-
215516c77SSepherosa Ziehau  * Copyright (c) 2010-2012 Citrix Inc.
393b4e111SSepherosa Ziehau  * Copyright (c) 2009-2012,2016-2017 Microsoft Corp.
415516c77SSepherosa Ziehau  * Copyright (c) 2012 NetApp Inc.
515516c77SSepherosa Ziehau  * All rights reserved.
615516c77SSepherosa Ziehau  *
715516c77SSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
815516c77SSepherosa Ziehau  * modification, are permitted provided that the following conditions
915516c77SSepherosa Ziehau  * are met:
1015516c77SSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
1115516c77SSepherosa Ziehau  *    notice unmodified, this list of conditions, and the following
1215516c77SSepherosa Ziehau  *    disclaimer.
1315516c77SSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
1415516c77SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in the
1515516c77SSepherosa Ziehau  *    documentation and/or other materials provided with the distribution.
1615516c77SSepherosa Ziehau  *
1715516c77SSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1815516c77SSepherosa Ziehau  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1915516c77SSepherosa Ziehau  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2015516c77SSepherosa Ziehau  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2115516c77SSepherosa Ziehau  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2215516c77SSepherosa Ziehau  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2315516c77SSepherosa Ziehau  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2415516c77SSepherosa Ziehau  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2515516c77SSepherosa Ziehau  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2615516c77SSepherosa Ziehau  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2715516c77SSepherosa Ziehau  */
2815516c77SSepherosa Ziehau 
2915516c77SSepherosa Ziehau /*-
3015516c77SSepherosa Ziehau  * Copyright (c) 2004-2006 Kip Macy
3115516c77SSepherosa Ziehau  * All rights reserved.
3215516c77SSepherosa Ziehau  *
3315516c77SSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
3415516c77SSepherosa Ziehau  * modification, are permitted provided that the following conditions
3515516c77SSepherosa Ziehau  * are met:
3615516c77SSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
3715516c77SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer.
3815516c77SSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
3915516c77SSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in the
4015516c77SSepherosa Ziehau  *    documentation and/or other materials provided with the distribution.
4115516c77SSepherosa Ziehau  *
4215516c77SSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4315516c77SSepherosa Ziehau  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4415516c77SSepherosa Ziehau  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4515516c77SSepherosa Ziehau  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4615516c77SSepherosa Ziehau  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4715516c77SSepherosa Ziehau  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4815516c77SSepherosa Ziehau  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4915516c77SSepherosa Ziehau  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5015516c77SSepherosa Ziehau  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5115516c77SSepherosa Ziehau  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5215516c77SSepherosa Ziehau  * SUCH DAMAGE.
5315516c77SSepherosa Ziehau  */
5415516c77SSepherosa Ziehau 
5515516c77SSepherosa Ziehau #include <sys/cdefs.h>
5615516c77SSepherosa Ziehau __FBSDID("$FreeBSD$");
5715516c77SSepherosa Ziehau 
5834d68912SSepherosa Ziehau #include "opt_hn.h"
5915516c77SSepherosa Ziehau #include "opt_inet6.h"
6015516c77SSepherosa Ziehau #include "opt_inet.h"
6134d68912SSepherosa Ziehau #include "opt_rss.h"
6215516c77SSepherosa Ziehau 
6315516c77SSepherosa Ziehau #include <sys/param.h>
6435203574SSepherosa Ziehau #include <sys/systm.h>
6515516c77SSepherosa Ziehau #include <sys/bus.h>
662be266caSSepherosa Ziehau #include <sys/counter.h>
6715516c77SSepherosa Ziehau #include <sys/kernel.h>
6815516c77SSepherosa Ziehau #include <sys/limits.h>
6915516c77SSepherosa Ziehau #include <sys/malloc.h>
7015516c77SSepherosa Ziehau #include <sys/mbuf.h>
7115516c77SSepherosa Ziehau #include <sys/module.h>
7215516c77SSepherosa Ziehau #include <sys/queue.h>
7315516c77SSepherosa Ziehau #include <sys/lock.h>
74499c3e17SSepherosa Ziehau #include <sys/rmlock.h>
75499c3e17SSepherosa Ziehau #include <sys/sbuf.h>
7615516c77SSepherosa Ziehau #include <sys/smp.h>
7715516c77SSepherosa Ziehau #include <sys/socket.h>
7815516c77SSepherosa Ziehau #include <sys/sockio.h>
7915516c77SSepherosa Ziehau #include <sys/sx.h>
8015516c77SSepherosa Ziehau #include <sys/sysctl.h>
8115516c77SSepherosa Ziehau #include <sys/taskqueue.h>
8215516c77SSepherosa Ziehau #include <sys/buf_ring.h>
835bdfd3fdSDexuan Cui #include <sys/eventhandler.h>
8415516c77SSepherosa Ziehau 
8515516c77SSepherosa Ziehau #include <machine/atomic.h>
8615516c77SSepherosa Ziehau #include <machine/in_cksum.h>
8715516c77SSepherosa Ziehau 
8815516c77SSepherosa Ziehau #include <net/bpf.h>
8915516c77SSepherosa Ziehau #include <net/ethernet.h>
9015516c77SSepherosa Ziehau #include <net/if.h>
915bdfd3fdSDexuan Cui #include <net/if_dl.h>
9215516c77SSepherosa Ziehau #include <net/if_media.h>
9315516c77SSepherosa Ziehau #include <net/if_types.h>
9415516c77SSepherosa Ziehau #include <net/if_var.h>
9515516c77SSepherosa Ziehau #include <net/rndis.h>
9634d68912SSepherosa Ziehau #ifdef RSS
9734d68912SSepherosa Ziehau #include <net/rss_config.h>
9834d68912SSepherosa Ziehau #endif
9915516c77SSepherosa Ziehau 
10015516c77SSepherosa Ziehau #include <netinet/in_systm.h>
10115516c77SSepherosa Ziehau #include <netinet/in.h>
10215516c77SSepherosa Ziehau #include <netinet/ip.h>
10315516c77SSepherosa Ziehau #include <netinet/ip6.h>
10415516c77SSepherosa Ziehau #include <netinet/tcp.h>
10515516c77SSepherosa Ziehau #include <netinet/tcp_lro.h>
10615516c77SSepherosa Ziehau #include <netinet/udp.h>
10715516c77SSepherosa Ziehau 
10815516c77SSepherosa Ziehau #include <dev/hyperv/include/hyperv.h>
10915516c77SSepherosa Ziehau #include <dev/hyperv/include/hyperv_busdma.h>
11015516c77SSepherosa Ziehau #include <dev/hyperv/include/vmbus.h>
11115516c77SSepherosa Ziehau #include <dev/hyperv/include/vmbus_xact.h>
11215516c77SSepherosa Ziehau 
11315516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/ndis.h>
11415516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/if_hnreg.h>
11515516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/if_hnvar.h>
11615516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/hn_nvs.h>
11715516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/hn_rndis.h>
11815516c77SSepherosa Ziehau 
11915516c77SSepherosa Ziehau #include "vmbus_if.h"
12015516c77SSepherosa Ziehau 
12123bf9e15SSepherosa Ziehau #define HN_IFSTART_SUPPORT
12223bf9e15SSepherosa Ziehau 
12315516c77SSepherosa Ziehau #define HN_RING_CNT_DEF_MAX		8
12415516c77SSepherosa Ziehau 
125499c3e17SSepherosa Ziehau #define HN_VFMAP_SIZE_DEF		8
126499c3e17SSepherosa Ziehau 
1279c6cae24SSepherosa Ziehau #define HN_XPNT_VF_ATTWAIT_MIN		2	/* seconds */
1289c6cae24SSepherosa Ziehau 
12915516c77SSepherosa Ziehau /* YYY should get it from the underlying channel */
13015516c77SSepherosa Ziehau #define HN_TX_DESC_CNT			512
13115516c77SSepherosa Ziehau 
13215516c77SSepherosa Ziehau #define HN_RNDIS_PKT_LEN					\
13315516c77SSepherosa Ziehau 	(sizeof(struct rndis_packet_msg) +			\
13415516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(HN_NDIS_HASH_VALUE_SIZE) +	\
13515516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_VLAN_INFO_SIZE) +		\
13615516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_LSO2_INFO_SIZE) +		\
13715516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_TXCSUM_INFO_SIZE))
13815516c77SSepherosa Ziehau #define HN_RNDIS_PKT_BOUNDARY		PAGE_SIZE
13915516c77SSepherosa Ziehau #define HN_RNDIS_PKT_ALIGN		CACHE_LINE_SIZE
14015516c77SSepherosa Ziehau 
14115516c77SSepherosa Ziehau #define HN_TX_DATA_BOUNDARY		PAGE_SIZE
14215516c77SSepherosa Ziehau #define HN_TX_DATA_MAXSIZE		IP_MAXPACKET
14315516c77SSepherosa Ziehau #define HN_TX_DATA_SEGSIZE		PAGE_SIZE
14415516c77SSepherosa Ziehau /* -1 for RNDIS packet message */
14515516c77SSepherosa Ziehau #define HN_TX_DATA_SEGCNT_MAX		(HN_GPACNT_MAX - 1)
14615516c77SSepherosa Ziehau 
14715516c77SSepherosa Ziehau #define HN_DIRECT_TX_SIZE_DEF		128
14815516c77SSepherosa Ziehau 
14915516c77SSepherosa Ziehau #define HN_EARLY_TXEOF_THRESH		8
15015516c77SSepherosa Ziehau 
15115516c77SSepherosa Ziehau #define HN_PKTBUF_LEN_DEF		(16 * 1024)
15215516c77SSepherosa Ziehau 
15315516c77SSepherosa Ziehau #define HN_LROENT_CNT_DEF		128
15415516c77SSepherosa Ziehau 
15515516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MULTIRX_DEF	(12 * ETHERMTU)
15615516c77SSepherosa Ziehau #define HN_LRO_LENLIM_DEF		(25 * ETHERMTU)
15715516c77SSepherosa Ziehau /* YYY 2*MTU is a bit rough, but should be good enough. */
15815516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MIN(ifp)		(2 * (ifp)->if_mtu)
15915516c77SSepherosa Ziehau 
16015516c77SSepherosa Ziehau #define HN_LRO_ACKCNT_DEF		1
16115516c77SSepherosa Ziehau 
16215516c77SSepherosa Ziehau #define HN_LOCK_INIT(sc)		\
16315516c77SSepherosa Ziehau 	sx_init(&(sc)->hn_lock, device_get_nameunit((sc)->hn_dev))
16415516c77SSepherosa Ziehau #define HN_LOCK_DESTROY(sc)		sx_destroy(&(sc)->hn_lock)
16515516c77SSepherosa Ziehau #define HN_LOCK_ASSERT(sc)		sx_assert(&(sc)->hn_lock, SA_XLOCKED)
166fdc4f478SSepherosa Ziehau #define HN_LOCK(sc)					\
167fdc4f478SSepherosa Ziehau do {							\
168fdc4f478SSepherosa Ziehau 	while (sx_try_xlock(&(sc)->hn_lock) == 0)	\
169fdc4f478SSepherosa Ziehau 		DELAY(1000);				\
170fdc4f478SSepherosa Ziehau } while (0)
17115516c77SSepherosa Ziehau #define HN_UNLOCK(sc)			sx_xunlock(&(sc)->hn_lock)
17215516c77SSepherosa Ziehau 
17315516c77SSepherosa Ziehau #define HN_CSUM_IP_MASK			(CSUM_IP | CSUM_IP_TCP | CSUM_IP_UDP)
17415516c77SSepherosa Ziehau #define HN_CSUM_IP6_MASK		(CSUM_IP6_TCP | CSUM_IP6_UDP)
17515516c77SSepherosa Ziehau #define HN_CSUM_IP_HWASSIST(sc)		\
17615516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP_MASK)
17715516c77SSepherosa Ziehau #define HN_CSUM_IP6_HWASSIST(sc)	\
17815516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP6_MASK)
17915516c77SSepherosa Ziehau 
180dc13fee6SSepherosa Ziehau #define HN_PKTSIZE_MIN(align)		\
181dc13fee6SSepherosa Ziehau 	roundup2(ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN - ETHER_CRC_LEN + \
182dc13fee6SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN, (align))
183dc13fee6SSepherosa Ziehau #define HN_PKTSIZE(m, align)		\
184dc13fee6SSepherosa Ziehau 	roundup2((m)->m_pkthdr.len + HN_RNDIS_PKT_LEN, (align))
185dc13fee6SSepherosa Ziehau 
18634d68912SSepherosa Ziehau #ifdef RSS
18734d68912SSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	rss_getcpu((idx) % rss_getnumbuckets())
18834d68912SSepherosa Ziehau #else
1890e11868dSSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	(((sc)->hn_cpu + (idx)) % mp_ncpus)
19034d68912SSepherosa Ziehau #endif
1910e11868dSSepherosa Ziehau 
19215516c77SSepherosa Ziehau struct hn_txdesc {
19315516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
19415516c77SSepherosa Ziehau 	SLIST_ENTRY(hn_txdesc)		link;
19515516c77SSepherosa Ziehau #endif
196dc13fee6SSepherosa Ziehau 	STAILQ_ENTRY(hn_txdesc)		agg_link;
197dc13fee6SSepherosa Ziehau 
198dc13fee6SSepherosa Ziehau 	/* Aggregated txdescs, in sending order. */
199dc13fee6SSepherosa Ziehau 	STAILQ_HEAD(, hn_txdesc)	agg_list;
200dc13fee6SSepherosa Ziehau 
201dc13fee6SSepherosa Ziehau 	/* The oldest packet, if transmission aggregation happens. */
20215516c77SSepherosa Ziehau 	struct mbuf			*m;
20315516c77SSepherosa Ziehau 	struct hn_tx_ring		*txr;
20415516c77SSepherosa Ziehau 	int				refs;
20515516c77SSepherosa Ziehau 	uint32_t			flags;	/* HN_TXD_FLAG_ */
20615516c77SSepherosa Ziehau 	struct hn_nvs_sendctx		send_ctx;
20715516c77SSepherosa Ziehau 	uint32_t			chim_index;
20815516c77SSepherosa Ziehau 	int				chim_size;
20915516c77SSepherosa Ziehau 
21015516c77SSepherosa Ziehau 	bus_dmamap_t			data_dmap;
21115516c77SSepherosa Ziehau 
21215516c77SSepherosa Ziehau 	bus_addr_t			rndis_pkt_paddr;
21315516c77SSepherosa Ziehau 	struct rndis_packet_msg		*rndis_pkt;
21415516c77SSepherosa Ziehau 	bus_dmamap_t			rndis_pkt_dmap;
21515516c77SSepherosa Ziehau };
21615516c77SSepherosa Ziehau 
21715516c77SSepherosa Ziehau #define HN_TXD_FLAG_ONLIST		0x0001
21815516c77SSepherosa Ziehau #define HN_TXD_FLAG_DMAMAP		0x0002
219dc13fee6SSepherosa Ziehau #define HN_TXD_FLAG_ONAGG		0x0004
22015516c77SSepherosa Ziehau 
22115516c77SSepherosa Ziehau struct hn_rxinfo {
22215516c77SSepherosa Ziehau 	uint32_t			vlan_info;
22315516c77SSepherosa Ziehau 	uint32_t			csum_info;
22415516c77SSepherosa Ziehau 	uint32_t			hash_info;
22515516c77SSepherosa Ziehau 	uint32_t			hash_value;
22615516c77SSepherosa Ziehau };
22715516c77SSepherosa Ziehau 
228962f0357SSepherosa Ziehau struct hn_rxvf_setarg {
2295bdfd3fdSDexuan Cui 	struct hn_rx_ring	*rxr;
230962f0357SSepherosa Ziehau 	struct ifnet		*vf_ifp;
2315bdfd3fdSDexuan Cui };
2325bdfd3fdSDexuan Cui 
23315516c77SSepherosa Ziehau #define HN_RXINFO_VLAN			0x0001
23415516c77SSepherosa Ziehau #define HN_RXINFO_CSUM			0x0002
23515516c77SSepherosa Ziehau #define HN_RXINFO_HASHINF		0x0004
23615516c77SSepherosa Ziehau #define HN_RXINFO_HASHVAL		0x0008
23715516c77SSepherosa Ziehau #define HN_RXINFO_ALL			\
23815516c77SSepherosa Ziehau 	(HN_RXINFO_VLAN |		\
23915516c77SSepherosa Ziehau 	 HN_RXINFO_CSUM |		\
24015516c77SSepherosa Ziehau 	 HN_RXINFO_HASHINF |		\
24115516c77SSepherosa Ziehau 	 HN_RXINFO_HASHVAL)
24215516c77SSepherosa Ziehau 
24315516c77SSepherosa Ziehau #define HN_NDIS_VLAN_INFO_INVALID	0xffffffff
24415516c77SSepherosa Ziehau #define HN_NDIS_RXCSUM_INFO_INVALID	0
24515516c77SSepherosa Ziehau #define HN_NDIS_HASH_INFO_INVALID	0
24615516c77SSepherosa Ziehau 
24715516c77SSepherosa Ziehau static int			hn_probe(device_t);
24815516c77SSepherosa Ziehau static int			hn_attach(device_t);
24915516c77SSepherosa Ziehau static int			hn_detach(device_t);
25015516c77SSepherosa Ziehau static int			hn_shutdown(device_t);
25115516c77SSepherosa Ziehau static void			hn_chan_callback(struct vmbus_channel *,
25215516c77SSepherosa Ziehau 				    void *);
25315516c77SSepherosa Ziehau 
25415516c77SSepherosa Ziehau static void			hn_init(void *);
25515516c77SSepherosa Ziehau static int			hn_ioctl(struct ifnet *, u_long, caddr_t);
25623bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
25715516c77SSepherosa Ziehau static void			hn_start(struct ifnet *);
25823bf9e15SSepherosa Ziehau #endif
25915516c77SSepherosa Ziehau static int			hn_transmit(struct ifnet *, struct mbuf *);
26015516c77SSepherosa Ziehau static void			hn_xmit_qflush(struct ifnet *);
26115516c77SSepherosa Ziehau static int			hn_ifmedia_upd(struct ifnet *);
26215516c77SSepherosa Ziehau static void			hn_ifmedia_sts(struct ifnet *,
26315516c77SSepherosa Ziehau 				    struct ifmediareq *);
26415516c77SSepherosa Ziehau 
265499c3e17SSepherosa Ziehau static void			hn_ifnet_event(void *, struct ifnet *, int);
266499c3e17SSepherosa Ziehau static void			hn_ifaddr_event(void *, struct ifnet *);
267499c3e17SSepherosa Ziehau static void			hn_ifnet_attevent(void *, struct ifnet *);
268499c3e17SSepherosa Ziehau static void			hn_ifnet_detevent(void *, struct ifnet *);
2699c6cae24SSepherosa Ziehau static void			hn_ifnet_lnkevent(void *, struct ifnet *, int);
270499c3e17SSepherosa Ziehau 
271962f0357SSepherosa Ziehau static bool			hn_ismyvf(const struct hn_softc *,
272962f0357SSepherosa Ziehau 				    const struct ifnet *);
273962f0357SSepherosa Ziehau static void			hn_rxvf_change(struct hn_softc *,
274962f0357SSepherosa Ziehau 				    struct ifnet *, bool);
275962f0357SSepherosa Ziehau static void			hn_rxvf_set(struct hn_softc *, struct ifnet *);
276962f0357SSepherosa Ziehau static void			hn_rxvf_set_task(void *, int);
2779c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_input(struct ifnet *, struct mbuf *);
2789c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_iocsetflags(struct hn_softc *);
2799c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_iocsetcaps(struct hn_softc *,
2809c6cae24SSepherosa Ziehau 				    struct ifreq *);
2819c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_saveifflags(struct hn_softc *);
2829c6cae24SSepherosa Ziehau static bool			hn_xpnt_vf_isready(struct hn_softc *);
2839c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_setready(struct hn_softc *);
2849c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_init_taskfunc(void *, int);
2859c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_init(struct hn_softc *);
286a97fff19SSepherosa Ziehau static void			hn_xpnt_vf_setenable(struct hn_softc *);
287a97fff19SSepherosa Ziehau static void			hn_xpnt_vf_setdisable(struct hn_softc *, bool);
288642ec226SSepherosa Ziehau static void			hn_vf_rss_fixup(struct hn_softc *, bool);
289642ec226SSepherosa Ziehau static void			hn_vf_rss_restore(struct hn_softc *);
290962f0357SSepherosa Ziehau 
29115516c77SSepherosa Ziehau static int			hn_rndis_rxinfo(const void *, int,
29215516c77SSepherosa Ziehau 				    struct hn_rxinfo *);
29315516c77SSepherosa Ziehau static void			hn_rndis_rx_data(struct hn_rx_ring *,
29415516c77SSepherosa Ziehau 				    const void *, int);
29515516c77SSepherosa Ziehau static void			hn_rndis_rx_status(struct hn_softc *,
29615516c77SSepherosa Ziehau 				    const void *, int);
297b3b75d9cSSepherosa Ziehau static void			hn_rndis_init_fixat(struct hn_softc *, int);
29815516c77SSepherosa Ziehau 
29915516c77SSepherosa Ziehau static void			hn_nvs_handle_notify(struct hn_softc *,
30015516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
30115516c77SSepherosa Ziehau static void			hn_nvs_handle_comp(struct hn_softc *,
30215516c77SSepherosa Ziehau 				    struct vmbus_channel *,
30315516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
30415516c77SSepherosa Ziehau static void			hn_nvs_handle_rxbuf(struct hn_rx_ring *,
30515516c77SSepherosa Ziehau 				    struct vmbus_channel *,
30615516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
30715516c77SSepherosa Ziehau static void			hn_nvs_ack_rxbuf(struct hn_rx_ring *,
30815516c77SSepherosa Ziehau 				    struct vmbus_channel *, uint64_t);
30915516c77SSepherosa Ziehau 
31015516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
31115516c77SSepherosa Ziehau static int			hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS);
31215516c77SSepherosa Ziehau static int			hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS);
31315516c77SSepherosa Ziehau #endif
31415516c77SSepherosa Ziehau static int			hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS);
31515516c77SSepherosa Ziehau static int			hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS);
31615516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
31715516c77SSepherosa Ziehau static int			hn_rx_stat_int_sysctl(SYSCTL_HANDLER_ARGS);
31815516c77SSepherosa Ziehau #else
31915516c77SSepherosa Ziehau static int			hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS);
32015516c77SSepherosa Ziehau #endif
32115516c77SSepherosa Ziehau static int			hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
32215516c77SSepherosa Ziehau static int			hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
32315516c77SSepherosa Ziehau static int			hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS);
32415516c77SSepherosa Ziehau static int			hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS);
32515516c77SSepherosa Ziehau static int			hn_caps_sysctl(SYSCTL_HANDLER_ARGS);
32615516c77SSepherosa Ziehau static int			hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS);
32715516c77SSepherosa Ziehau static int			hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS);
32834d68912SSepherosa Ziehau #ifndef RSS
32915516c77SSepherosa Ziehau static int			hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS);
33015516c77SSepherosa Ziehau static int			hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS);
33134d68912SSepherosa Ziehau #endif
33215516c77SSepherosa Ziehau static int			hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS);
333642ec226SSepherosa Ziehau static int			hn_rss_hcap_sysctl(SYSCTL_HANDLER_ARGS);
334642ec226SSepherosa Ziehau static int			hn_rss_mbuf_sysctl(SYSCTL_HANDLER_ARGS);
335dc13fee6SSepherosa Ziehau static int			hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS);
336dc13fee6SSepherosa Ziehau static int			hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS);
337dc13fee6SSepherosa Ziehau static int			hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS);
338dc13fee6SSepherosa Ziehau static int			hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
3396c1204dfSSepherosa Ziehau static int			hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
34040d60d6eSDexuan Cui static int			hn_vf_sysctl(SYSCTL_HANDLER_ARGS);
341499c3e17SSepherosa Ziehau static int			hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS);
342499c3e17SSepherosa Ziehau static int			hn_vflist_sysctl(SYSCTL_HANDLER_ARGS);
343499c3e17SSepherosa Ziehau static int			hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS);
3449c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS);
3459c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS);
34615516c77SSepherosa Ziehau 
3475bdfd3fdSDexuan Cui static void			hn_stop(struct hn_softc *, bool);
34815516c77SSepherosa Ziehau static void			hn_init_locked(struct hn_softc *);
34915516c77SSepherosa Ziehau static int			hn_chan_attach(struct hn_softc *,
35015516c77SSepherosa Ziehau 				    struct vmbus_channel *);
35115516c77SSepherosa Ziehau static void			hn_chan_detach(struct hn_softc *,
35215516c77SSepherosa Ziehau 				    struct vmbus_channel *);
35315516c77SSepherosa Ziehau static int			hn_attach_subchans(struct hn_softc *);
35415516c77SSepherosa Ziehau static void			hn_detach_allchans(struct hn_softc *);
35515516c77SSepherosa Ziehau static void			hn_chan_rollup(struct hn_rx_ring *,
35615516c77SSepherosa Ziehau 				    struct hn_tx_ring *);
35715516c77SSepherosa Ziehau static void			hn_set_ring_inuse(struct hn_softc *, int);
35815516c77SSepherosa Ziehau static int			hn_synth_attach(struct hn_softc *, int);
35915516c77SSepherosa Ziehau static void			hn_synth_detach(struct hn_softc *);
36015516c77SSepherosa Ziehau static int			hn_synth_alloc_subchans(struct hn_softc *,
36115516c77SSepherosa Ziehau 				    int *);
3622494d735SSepherosa Ziehau static bool			hn_synth_attachable(const struct hn_softc *);
36315516c77SSepherosa Ziehau static void			hn_suspend(struct hn_softc *);
36415516c77SSepherosa Ziehau static void			hn_suspend_data(struct hn_softc *);
36515516c77SSepherosa Ziehau static void			hn_suspend_mgmt(struct hn_softc *);
36615516c77SSepherosa Ziehau static void			hn_resume(struct hn_softc *);
36715516c77SSepherosa Ziehau static void			hn_resume_data(struct hn_softc *);
36815516c77SSepherosa Ziehau static void			hn_resume_mgmt(struct hn_softc *);
36915516c77SSepherosa Ziehau static void			hn_suspend_mgmt_taskfunc(void *, int);
37025641fc7SSepherosa Ziehau static void			hn_chan_drain(struct hn_softc *,
37125641fc7SSepherosa Ziehau 				    struct vmbus_channel *);
372b3b75d9cSSepherosa Ziehau static void			hn_disable_rx(struct hn_softc *);
373b3b75d9cSSepherosa Ziehau static void			hn_drain_rxtx(struct hn_softc *, int);
3746c1204dfSSepherosa Ziehau static void			hn_polling(struct hn_softc *, u_int);
3756c1204dfSSepherosa Ziehau static void			hn_chan_polling(struct vmbus_channel *, u_int);
3769c6cae24SSepherosa Ziehau static void			hn_mtu_change_fixup(struct hn_softc *);
37715516c77SSepherosa Ziehau 
37815516c77SSepherosa Ziehau static void			hn_update_link_status(struct hn_softc *);
37915516c77SSepherosa Ziehau static void			hn_change_network(struct hn_softc *);
38015516c77SSepherosa Ziehau static void			hn_link_taskfunc(void *, int);
38115516c77SSepherosa Ziehau static void			hn_netchg_init_taskfunc(void *, int);
38215516c77SSepherosa Ziehau static void			hn_netchg_status_taskfunc(void *, int);
38315516c77SSepherosa Ziehau static void			hn_link_status(struct hn_softc *);
38415516c77SSepherosa Ziehau 
38515516c77SSepherosa Ziehau static int			hn_create_rx_data(struct hn_softc *, int);
38615516c77SSepherosa Ziehau static void			hn_destroy_rx_data(struct hn_softc *);
38715516c77SSepherosa Ziehau static int			hn_check_iplen(const struct mbuf *, int);
388db76829bSSepherosa Ziehau static void			hn_rxpkt_proto(const struct mbuf *, int *, int *);
389f1b0a43fSSepherosa Ziehau static int			hn_set_rxfilter(struct hn_softc *, uint32_t);
390c08f7b2cSSepherosa Ziehau static int			hn_rxfilter_config(struct hn_softc *);
39115516c77SSepherosa Ziehau static int			hn_rss_reconfig(struct hn_softc *);
392afd4971bSSepherosa Ziehau static void			hn_rss_ind_fixup(struct hn_softc *);
393642ec226SSepherosa Ziehau static void			hn_rss_mbuf_hash(struct hn_softc *, uint32_t);
39415516c77SSepherosa Ziehau static int			hn_rxpkt(struct hn_rx_ring *, const void *,
39515516c77SSepherosa Ziehau 				    int, const struct hn_rxinfo *);
396642ec226SSepherosa Ziehau static uint32_t			hn_rss_type_fromndis(uint32_t);
397642ec226SSepherosa Ziehau static uint32_t			hn_rss_type_tondis(uint32_t);
39815516c77SSepherosa Ziehau 
39915516c77SSepherosa Ziehau static int			hn_tx_ring_create(struct hn_softc *, int);
40015516c77SSepherosa Ziehau static void			hn_tx_ring_destroy(struct hn_tx_ring *);
40115516c77SSepherosa Ziehau static int			hn_create_tx_data(struct hn_softc *, int);
40215516c77SSepherosa Ziehau static void			hn_fixup_tx_data(struct hn_softc *);
403db76829bSSepherosa Ziehau static void			hn_fixup_rx_data(struct hn_softc *);
40415516c77SSepherosa Ziehau static void			hn_destroy_tx_data(struct hn_softc *);
40515516c77SSepherosa Ziehau static void			hn_txdesc_dmamap_destroy(struct hn_txdesc *);
40625641fc7SSepherosa Ziehau static void			hn_txdesc_gc(struct hn_tx_ring *,
40725641fc7SSepherosa Ziehau 				    struct hn_txdesc *);
408dc13fee6SSepherosa Ziehau static int			hn_encap(struct ifnet *, struct hn_tx_ring *,
40915516c77SSepherosa Ziehau 				    struct hn_txdesc *, struct mbuf **);
41015516c77SSepherosa Ziehau static int			hn_txpkt(struct ifnet *, struct hn_tx_ring *,
41115516c77SSepherosa Ziehau 				    struct hn_txdesc *);
41215516c77SSepherosa Ziehau static void			hn_set_chim_size(struct hn_softc *, int);
41315516c77SSepherosa Ziehau static void			hn_set_tso_maxsize(struct hn_softc *, int, int);
41415516c77SSepherosa Ziehau static bool			hn_tx_ring_pending(struct hn_tx_ring *);
41515516c77SSepherosa Ziehau static void			hn_tx_ring_qflush(struct hn_tx_ring *);
41615516c77SSepherosa Ziehau static void			hn_resume_tx(struct hn_softc *, int);
417dc13fee6SSepherosa Ziehau static void			hn_set_txagg(struct hn_softc *);
418dc13fee6SSepherosa Ziehau static void			*hn_try_txagg(struct ifnet *,
419dc13fee6SSepherosa Ziehau 				    struct hn_tx_ring *, struct hn_txdesc *,
420dc13fee6SSepherosa Ziehau 				    int);
42115516c77SSepherosa Ziehau static int			hn_get_txswq_depth(const struct hn_tx_ring *);
42215516c77SSepherosa Ziehau static void			hn_txpkt_done(struct hn_nvs_sendctx *,
42315516c77SSepherosa Ziehau 				    struct hn_softc *, struct vmbus_channel *,
42415516c77SSepherosa Ziehau 				    const void *, int);
42515516c77SSepherosa Ziehau static int			hn_txpkt_sglist(struct hn_tx_ring *,
42615516c77SSepherosa Ziehau 				    struct hn_txdesc *);
42715516c77SSepherosa Ziehau static int			hn_txpkt_chim(struct hn_tx_ring *,
42815516c77SSepherosa Ziehau 				    struct hn_txdesc *);
42915516c77SSepherosa Ziehau static int			hn_xmit(struct hn_tx_ring *, int);
43015516c77SSepherosa Ziehau static void			hn_xmit_taskfunc(void *, int);
43115516c77SSepherosa Ziehau static void			hn_xmit_txeof(struct hn_tx_ring *);
43215516c77SSepherosa Ziehau static void			hn_xmit_txeof_taskfunc(void *, int);
43323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
43415516c77SSepherosa Ziehau static int			hn_start_locked(struct hn_tx_ring *, int);
43515516c77SSepherosa Ziehau static void			hn_start_taskfunc(void *, int);
43615516c77SSepherosa Ziehau static void			hn_start_txeof(struct hn_tx_ring *);
43715516c77SSepherosa Ziehau static void			hn_start_txeof_taskfunc(void *, int);
43823bf9e15SSepherosa Ziehau #endif
43915516c77SSepherosa Ziehau 
44015516c77SSepherosa Ziehau SYSCTL_NODE(_hw, OID_AUTO, hn, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
44115516c77SSepherosa Ziehau     "Hyper-V network interface");
44215516c77SSepherosa Ziehau 
44315516c77SSepherosa Ziehau /* Trust tcp segements verification on host side. */
44415516c77SSepherosa Ziehau static int			hn_trust_hosttcp = 1;
44515516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hosttcp, CTLFLAG_RDTUN,
44615516c77SSepherosa Ziehau     &hn_trust_hosttcp, 0,
44715516c77SSepherosa Ziehau     "Trust tcp segement verification on host side, "
44815516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
44915516c77SSepherosa Ziehau 
45015516c77SSepherosa Ziehau /* Trust udp datagrams verification on host side. */
45115516c77SSepherosa Ziehau static int			hn_trust_hostudp = 1;
45215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostudp, CTLFLAG_RDTUN,
45315516c77SSepherosa Ziehau     &hn_trust_hostudp, 0,
45415516c77SSepherosa Ziehau     "Trust udp datagram verification on host side, "
45515516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
45615516c77SSepherosa Ziehau 
45715516c77SSepherosa Ziehau /* Trust ip packets verification on host side. */
45815516c77SSepherosa Ziehau static int			hn_trust_hostip = 1;
45915516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostip, CTLFLAG_RDTUN,
46015516c77SSepherosa Ziehau     &hn_trust_hostip, 0,
46115516c77SSepherosa Ziehau     "Trust ip packet verification on host side, "
46215516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
46315516c77SSepherosa Ziehau 
4642be266caSSepherosa Ziehau /*
4652be266caSSepherosa Ziehau  * Offload UDP/IPv4 checksum.
4662be266caSSepherosa Ziehau  */
4672be266caSSepherosa Ziehau static int			hn_enable_udp4cs = 1;
4682be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp4cs, CTLFLAG_RDTUN,
4692be266caSSepherosa Ziehau     &hn_enable_udp4cs, 0, "Offload UDP/IPv4 checksum");
4702be266caSSepherosa Ziehau 
4712be266caSSepherosa Ziehau /*
4722be266caSSepherosa Ziehau  * Offload UDP/IPv6 checksum.
4732be266caSSepherosa Ziehau  */
4742be266caSSepherosa Ziehau static int			hn_enable_udp6cs = 1;
4752be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp6cs, CTLFLAG_RDTUN,
4762be266caSSepherosa Ziehau     &hn_enable_udp6cs, 0, "Offload UDP/IPv6 checksum");
4772be266caSSepherosa Ziehau 
4782be266caSSepherosa Ziehau /* Stats. */
4792be266caSSepherosa Ziehau static counter_u64_t		hn_udpcs_fixup;
4802be266caSSepherosa Ziehau SYSCTL_COUNTER_U64(_hw_hn, OID_AUTO, udpcs_fixup, CTLFLAG_RW,
4812be266caSSepherosa Ziehau     &hn_udpcs_fixup, "# of UDP checksum fixup");
4822be266caSSepherosa Ziehau 
4832be266caSSepherosa Ziehau /*
4842be266caSSepherosa Ziehau  * See hn_set_hlen().
4852be266caSSepherosa Ziehau  *
4862be266caSSepherosa Ziehau  * This value is for Azure.  For Hyper-V, set this above
4872be266caSSepherosa Ziehau  * 65536 to disable UDP datagram checksum fixup.
4882be266caSSepherosa Ziehau  */
4892be266caSSepherosa Ziehau static int			hn_udpcs_fixup_mtu = 1420;
4902be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, udpcs_fixup_mtu, CTLFLAG_RWTUN,
4912be266caSSepherosa Ziehau     &hn_udpcs_fixup_mtu, 0, "UDP checksum fixup MTU threshold");
4922be266caSSepherosa Ziehau 
49315516c77SSepherosa Ziehau /* Limit TSO burst size */
49415516c77SSepherosa Ziehau static int			hn_tso_maxlen = IP_MAXPACKET;
49515516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tso_maxlen, CTLFLAG_RDTUN,
49615516c77SSepherosa Ziehau     &hn_tso_maxlen, 0, "TSO burst limit");
49715516c77SSepherosa Ziehau 
49815516c77SSepherosa Ziehau /* Limit chimney send size */
49915516c77SSepherosa Ziehau static int			hn_tx_chimney_size = 0;
50015516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_chimney_size, CTLFLAG_RDTUN,
50115516c77SSepherosa Ziehau     &hn_tx_chimney_size, 0, "Chimney send packet size limit");
50215516c77SSepherosa Ziehau 
50315516c77SSepherosa Ziehau /* Limit the size of packet for direct transmission */
50415516c77SSepherosa Ziehau static int			hn_direct_tx_size = HN_DIRECT_TX_SIZE_DEF;
50515516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, direct_tx_size, CTLFLAG_RDTUN,
50615516c77SSepherosa Ziehau     &hn_direct_tx_size, 0, "Size of the packet for direct transmission");
50715516c77SSepherosa Ziehau 
50815516c77SSepherosa Ziehau /* # of LRO entries per RX ring */
50915516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
51015516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
51115516c77SSepherosa Ziehau static int			hn_lro_entry_count = HN_LROENT_CNT_DEF;
51215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, lro_entry_count, CTLFLAG_RDTUN,
51315516c77SSepherosa Ziehau     &hn_lro_entry_count, 0, "LRO entry count");
51415516c77SSepherosa Ziehau #endif
51515516c77SSepherosa Ziehau #endif
51615516c77SSepherosa Ziehau 
517fdd0222aSSepherosa Ziehau static int			hn_tx_taskq_cnt = 1;
518fdd0222aSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_cnt, CTLFLAG_RDTUN,
519fdd0222aSSepherosa Ziehau     &hn_tx_taskq_cnt, 0, "# of TX taskqueues");
520fdd0222aSSepherosa Ziehau 
5210e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_INDEP	0
5220e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_GLOBAL	1
5230e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_EVTTQ	2
5240e11868dSSepherosa Ziehau 
5250e11868dSSepherosa Ziehau static int			hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
5260e11868dSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_mode, CTLFLAG_RDTUN,
5270e11868dSSepherosa Ziehau     &hn_tx_taskq_mode, 0, "TX taskqueue modes: "
5280e11868dSSepherosa Ziehau     "0 - independent, 1 - share global tx taskqs, 2 - share event taskqs");
5290e11868dSSepherosa Ziehau 
53015516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
53115516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 0;
53215516c77SSepherosa Ziehau #else
53315516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 1;
53415516c77SSepherosa Ziehau #endif
53515516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_txdesc_bufring, CTLFLAG_RD,
53615516c77SSepherosa Ziehau     &hn_use_txdesc_bufring, 0, "Use buf_ring for TX descriptors");
53715516c77SSepherosa Ziehau 
53823bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
53915516c77SSepherosa Ziehau /* Use ifnet.if_start instead of ifnet.if_transmit */
54015516c77SSepherosa Ziehau static int			hn_use_if_start = 0;
54115516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_if_start, CTLFLAG_RDTUN,
54215516c77SSepherosa Ziehau     &hn_use_if_start, 0, "Use if_start TX method");
54323bf9e15SSepherosa Ziehau #endif
54415516c77SSepherosa Ziehau 
54515516c77SSepherosa Ziehau /* # of channels to use */
54615516c77SSepherosa Ziehau static int			hn_chan_cnt = 0;
54715516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, chan_cnt, CTLFLAG_RDTUN,
54815516c77SSepherosa Ziehau     &hn_chan_cnt, 0,
54915516c77SSepherosa Ziehau     "# of channels to use; each channel has one RX ring and one TX ring");
55015516c77SSepherosa Ziehau 
55115516c77SSepherosa Ziehau /* # of transmit rings to use */
55215516c77SSepherosa Ziehau static int			hn_tx_ring_cnt = 0;
55315516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_ring_cnt, CTLFLAG_RDTUN,
55415516c77SSepherosa Ziehau     &hn_tx_ring_cnt, 0, "# of TX rings to use");
55515516c77SSepherosa Ziehau 
55615516c77SSepherosa Ziehau /* Software TX ring deptch */
55715516c77SSepherosa Ziehau static int			hn_tx_swq_depth = 0;
55815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_swq_depth, CTLFLAG_RDTUN,
55915516c77SSepherosa Ziehau     &hn_tx_swq_depth, 0, "Depth of IFQ or BUFRING");
56015516c77SSepherosa Ziehau 
56115516c77SSepherosa Ziehau /* Enable sorted LRO, and the depth of the per-channel mbuf queue */
56215516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
56315516c77SSepherosa Ziehau static u_int			hn_lro_mbufq_depth = 0;
56415516c77SSepherosa Ziehau SYSCTL_UINT(_hw_hn, OID_AUTO, lro_mbufq_depth, CTLFLAG_RDTUN,
56515516c77SSepherosa Ziehau     &hn_lro_mbufq_depth, 0, "Depth of LRO mbuf queue");
56615516c77SSepherosa Ziehau #endif
56715516c77SSepherosa Ziehau 
568dc13fee6SSepherosa Ziehau /* Packet transmission aggregation size limit */
569dc13fee6SSepherosa Ziehau static int			hn_tx_agg_size = -1;
570dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_size, CTLFLAG_RDTUN,
571dc13fee6SSepherosa Ziehau     &hn_tx_agg_size, 0, "Packet transmission aggregation size limit");
572dc13fee6SSepherosa Ziehau 
573dc13fee6SSepherosa Ziehau /* Packet transmission aggregation count limit */
574fa915c4dSSepherosa Ziehau static int			hn_tx_agg_pkts = -1;
575dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_pkts, CTLFLAG_RDTUN,
576dc13fee6SSepherosa Ziehau     &hn_tx_agg_pkts, 0, "Packet transmission aggregation packet limit");
577dc13fee6SSepherosa Ziehau 
578499c3e17SSepherosa Ziehau /* VF list */
579*7029da5cSPawel Biernacki SYSCTL_PROC(_hw_hn, OID_AUTO, vflist,
580*7029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 0, 0,
581*7029da5cSPawel Biernacki     hn_vflist_sysctl, "A",
582*7029da5cSPawel Biernacki     "VF list");
583499c3e17SSepherosa Ziehau 
584499c3e17SSepherosa Ziehau /* VF mapping */
585*7029da5cSPawel Biernacki SYSCTL_PROC(_hw_hn, OID_AUTO, vfmap,
586*7029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 0, 0,
587*7029da5cSPawel Biernacki     hn_vfmap_sysctl, "A",
588*7029da5cSPawel Biernacki     "VF mapping");
589499c3e17SSepherosa Ziehau 
5909c6cae24SSepherosa Ziehau /* Transparent VF */
59178e46963SSepherosa Ziehau static int			hn_xpnt_vf = 1;
5929c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_transparent, CTLFLAG_RDTUN,
5939c6cae24SSepherosa Ziehau     &hn_xpnt_vf, 0, "Transparent VF mod");
5949c6cae24SSepherosa Ziehau 
5959c6cae24SSepherosa Ziehau /* Accurate BPF support for Transparent VF */
5969c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf = 0;
5979c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_accbpf, CTLFLAG_RDTUN,
5989c6cae24SSepherosa Ziehau     &hn_xpnt_vf_accbpf, 0, "Accurate BPF for transparent VF");
5999c6cae24SSepherosa Ziehau 
6009c6cae24SSepherosa Ziehau /* Extra wait for transparent VF attach routing; unit seconds. */
6019c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
6029c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_attwait, CTLFLAG_RWTUN,
6039c6cae24SSepherosa Ziehau     &hn_xpnt_vf_attwait, 0,
6049c6cae24SSepherosa Ziehau     "Extra wait for transparent VF attach routing; unit: seconds");
6059c6cae24SSepherosa Ziehau 
60615516c77SSepherosa Ziehau static u_int			hn_cpu_index;	/* next CPU for channel */
607fdd0222aSSepherosa Ziehau static struct taskqueue		**hn_tx_taskque;/* shared TX taskqueues */
60815516c77SSepherosa Ziehau 
609499c3e17SSepherosa Ziehau static struct rmlock		hn_vfmap_lock;
610499c3e17SSepherosa Ziehau static int			hn_vfmap_size;
611499c3e17SSepherosa Ziehau static struct ifnet		**hn_vfmap;
612499c3e17SSepherosa Ziehau 
61334d68912SSepherosa Ziehau #ifndef RSS
61415516c77SSepherosa Ziehau static const uint8_t
61515516c77SSepherosa Ziehau hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
61615516c77SSepherosa Ziehau 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
61715516c77SSepherosa Ziehau 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
61815516c77SSepherosa Ziehau 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
61915516c77SSepherosa Ziehau 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
62015516c77SSepherosa Ziehau 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
62115516c77SSepherosa Ziehau };
62234d68912SSepherosa Ziehau #endif	/* !RSS */
62315516c77SSepherosa Ziehau 
624c2d50b26SSepherosa Ziehau static const struct hyperv_guid	hn_guid = {
625c2d50b26SSepherosa Ziehau 	.hv_guid = {
626c2d50b26SSepherosa Ziehau 	    0x63, 0x51, 0x61, 0xf8, 0x3e, 0xdf, 0xc5, 0x46,
627c2d50b26SSepherosa Ziehau 	    0x91, 0x3f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e }
628c2d50b26SSepherosa Ziehau };
629c2d50b26SSepherosa Ziehau 
63015516c77SSepherosa Ziehau static device_method_t hn_methods[] = {
63115516c77SSepherosa Ziehau 	/* Device interface */
63215516c77SSepherosa Ziehau 	DEVMETHOD(device_probe,		hn_probe),
63315516c77SSepherosa Ziehau 	DEVMETHOD(device_attach,	hn_attach),
63415516c77SSepherosa Ziehau 	DEVMETHOD(device_detach,	hn_detach),
63515516c77SSepherosa Ziehau 	DEVMETHOD(device_shutdown,	hn_shutdown),
63615516c77SSepherosa Ziehau 	DEVMETHOD_END
63715516c77SSepherosa Ziehau };
63815516c77SSepherosa Ziehau 
63915516c77SSepherosa Ziehau static driver_t hn_driver = {
64015516c77SSepherosa Ziehau 	"hn",
64115516c77SSepherosa Ziehau 	hn_methods,
64215516c77SSepherosa Ziehau 	sizeof(struct hn_softc)
64315516c77SSepherosa Ziehau };
64415516c77SSepherosa Ziehau 
64515516c77SSepherosa Ziehau static devclass_t hn_devclass;
64615516c77SSepherosa Ziehau 
64715516c77SSepherosa Ziehau DRIVER_MODULE(hn, vmbus, hn_driver, hn_devclass, 0, 0);
64815516c77SSepherosa Ziehau MODULE_VERSION(hn, 1);
64915516c77SSepherosa Ziehau MODULE_DEPEND(hn, vmbus, 1, 1, 1);
65015516c77SSepherosa Ziehau 
65115516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
65215516c77SSepherosa Ziehau static void
65315516c77SSepherosa Ziehau hn_set_lro_lenlim(struct hn_softc *sc, int lenlim)
65415516c77SSepherosa Ziehau {
65515516c77SSepherosa Ziehau 	int i;
65615516c77SSepherosa Ziehau 
657a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
65815516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_length_lim = lenlim;
65915516c77SSepherosa Ziehau }
66015516c77SSepherosa Ziehau #endif
66115516c77SSepherosa Ziehau 
66215516c77SSepherosa Ziehau static int
66315516c77SSepherosa Ziehau hn_txpkt_sglist(struct hn_tx_ring *txr, struct hn_txdesc *txd)
66415516c77SSepherosa Ziehau {
66515516c77SSepherosa Ziehau 
66615516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
66715516c77SSepherosa Ziehau 	    txd->chim_size == 0, ("invalid rndis sglist txd"));
66815516c77SSepherosa Ziehau 	return (hn_nvs_send_rndis_sglist(txr->hn_chan, HN_NVS_RNDIS_MTYPE_DATA,
66915516c77SSepherosa Ziehau 	    &txd->send_ctx, txr->hn_gpa, txr->hn_gpa_cnt));
67015516c77SSepherosa Ziehau }
67115516c77SSepherosa Ziehau 
67215516c77SSepherosa Ziehau static int
67315516c77SSepherosa Ziehau hn_txpkt_chim(struct hn_tx_ring *txr, struct hn_txdesc *txd)
67415516c77SSepherosa Ziehau {
67515516c77SSepherosa Ziehau 	struct hn_nvs_rndis rndis;
67615516c77SSepherosa Ziehau 
67715516c77SSepherosa Ziehau 	KASSERT(txd->chim_index != HN_NVS_CHIM_IDX_INVALID &&
67815516c77SSepherosa Ziehau 	    txd->chim_size > 0, ("invalid rndis chim txd"));
67915516c77SSepherosa Ziehau 
68015516c77SSepherosa Ziehau 	rndis.nvs_type = HN_NVS_TYPE_RNDIS;
68115516c77SSepherosa Ziehau 	rndis.nvs_rndis_mtype = HN_NVS_RNDIS_MTYPE_DATA;
68215516c77SSepherosa Ziehau 	rndis.nvs_chim_idx = txd->chim_index;
68315516c77SSepherosa Ziehau 	rndis.nvs_chim_sz = txd->chim_size;
68415516c77SSepherosa Ziehau 
68515516c77SSepherosa Ziehau 	return (hn_nvs_send(txr->hn_chan, VMBUS_CHANPKT_FLAG_RC,
68615516c77SSepherosa Ziehau 	    &rndis, sizeof(rndis), &txd->send_ctx));
68715516c77SSepherosa Ziehau }
68815516c77SSepherosa Ziehau 
68915516c77SSepherosa Ziehau static __inline uint32_t
69015516c77SSepherosa Ziehau hn_chim_alloc(struct hn_softc *sc)
69115516c77SSepherosa Ziehau {
69215516c77SSepherosa Ziehau 	int i, bmap_cnt = sc->hn_chim_bmap_cnt;
69315516c77SSepherosa Ziehau 	u_long *bmap = sc->hn_chim_bmap;
69415516c77SSepherosa Ziehau 	uint32_t ret = HN_NVS_CHIM_IDX_INVALID;
69515516c77SSepherosa Ziehau 
69615516c77SSepherosa Ziehau 	for (i = 0; i < bmap_cnt; ++i) {
69715516c77SSepherosa Ziehau 		int idx;
69815516c77SSepherosa Ziehau 
69915516c77SSepherosa Ziehau 		idx = ffsl(~bmap[i]);
70015516c77SSepherosa Ziehau 		if (idx == 0)
70115516c77SSepherosa Ziehau 			continue;
70215516c77SSepherosa Ziehau 
70315516c77SSepherosa Ziehau 		--idx; /* ffsl is 1-based */
70415516c77SSepherosa Ziehau 		KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt,
70515516c77SSepherosa Ziehau 		    ("invalid i %d and idx %d", i, idx));
70615516c77SSepherosa Ziehau 
70715516c77SSepherosa Ziehau 		if (atomic_testandset_long(&bmap[i], idx))
70815516c77SSepherosa Ziehau 			continue;
70915516c77SSepherosa Ziehau 
71015516c77SSepherosa Ziehau 		ret = i * LONG_BIT + idx;
71115516c77SSepherosa Ziehau 		break;
71215516c77SSepherosa Ziehau 	}
71315516c77SSepherosa Ziehau 	return (ret);
71415516c77SSepherosa Ziehau }
71515516c77SSepherosa Ziehau 
71615516c77SSepherosa Ziehau static __inline void
71715516c77SSepherosa Ziehau hn_chim_free(struct hn_softc *sc, uint32_t chim_idx)
71815516c77SSepherosa Ziehau {
71915516c77SSepherosa Ziehau 	u_long mask;
72015516c77SSepherosa Ziehau 	uint32_t idx;
72115516c77SSepherosa Ziehau 
72215516c77SSepherosa Ziehau 	idx = chim_idx / LONG_BIT;
72315516c77SSepherosa Ziehau 	KASSERT(idx < sc->hn_chim_bmap_cnt,
72415516c77SSepherosa Ziehau 	    ("invalid chimney index 0x%x", chim_idx));
72515516c77SSepherosa Ziehau 
72615516c77SSepherosa Ziehau 	mask = 1UL << (chim_idx % LONG_BIT);
72715516c77SSepherosa Ziehau 	KASSERT(sc->hn_chim_bmap[idx] & mask,
72815516c77SSepherosa Ziehau 	    ("index bitmap 0x%lx, chimney index %u, "
72915516c77SSepherosa Ziehau 	     "bitmap idx %d, bitmask 0x%lx",
73015516c77SSepherosa Ziehau 	     sc->hn_chim_bmap[idx], chim_idx, idx, mask));
73115516c77SSepherosa Ziehau 
73215516c77SSepherosa Ziehau 	atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
73315516c77SSepherosa Ziehau }
73415516c77SSepherosa Ziehau 
735edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
736cc0c6ebcSSepherosa Ziehau 
737cc0c6ebcSSepherosa Ziehau #define PULLUP_HDR(m, len)				\
738cc0c6ebcSSepherosa Ziehau do {							\
739cc0c6ebcSSepherosa Ziehau 	if (__predict_false((m)->m_len < (len))) {	\
740cc0c6ebcSSepherosa Ziehau 		(m) = m_pullup((m), (len));		\
741cc0c6ebcSSepherosa Ziehau 		if ((m) == NULL)			\
742cc0c6ebcSSepherosa Ziehau 			return (NULL);			\
743cc0c6ebcSSepherosa Ziehau 	}						\
744cc0c6ebcSSepherosa Ziehau } while (0)
745cc0c6ebcSSepherosa Ziehau 
746edd3f315SSepherosa Ziehau /*
747edd3f315SSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
748edd3f315SSepherosa Ziehau  */
749edd3f315SSepherosa Ziehau static __inline struct mbuf *
750edd3f315SSepherosa Ziehau hn_tso_fixup(struct mbuf *m_head)
751edd3f315SSepherosa Ziehau {
752edd3f315SSepherosa Ziehau 	struct ether_vlan_header *evl;
753edd3f315SSepherosa Ziehau 	struct tcphdr *th;
754edd3f315SSepherosa Ziehau 	int ehlen;
755edd3f315SSepherosa Ziehau 
756edd3f315SSepherosa Ziehau 	KASSERT(M_WRITABLE(m_head), ("TSO mbuf not writable"));
757edd3f315SSepherosa Ziehau 
758edd3f315SSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
759edd3f315SSepherosa Ziehau 	evl = mtod(m_head, struct ether_vlan_header *);
760edd3f315SSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
761edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
762edd3f315SSepherosa Ziehau 	else
763edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
764c49d47daSSepherosa Ziehau 	m_head->m_pkthdr.l2hlen = ehlen;
765edd3f315SSepherosa Ziehau 
766edd3f315SSepherosa Ziehau #ifdef INET
767edd3f315SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
768edd3f315SSepherosa Ziehau 		struct ip *ip;
769edd3f315SSepherosa Ziehau 		int iphlen;
770edd3f315SSepherosa Ziehau 
771edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
772edd3f315SSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
773edd3f315SSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
774c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = iphlen;
775edd3f315SSepherosa Ziehau 
776edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
777edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + iphlen);
778edd3f315SSepherosa Ziehau 
779edd3f315SSepherosa Ziehau 		ip->ip_len = 0;
780edd3f315SSepherosa Ziehau 		ip->ip_sum = 0;
781edd3f315SSepherosa Ziehau 		th->th_sum = in_pseudo(ip->ip_src.s_addr,
782edd3f315SSepherosa Ziehau 		    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
783edd3f315SSepherosa Ziehau 	}
784edd3f315SSepherosa Ziehau #endif
785edd3f315SSepherosa Ziehau #if defined(INET6) && defined(INET)
786edd3f315SSepherosa Ziehau 	else
787edd3f315SSepherosa Ziehau #endif
788edd3f315SSepherosa Ziehau #ifdef INET6
789edd3f315SSepherosa Ziehau 	{
790edd3f315SSepherosa Ziehau 		struct ip6_hdr *ip6;
791edd3f315SSepherosa Ziehau 
792edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
793edd3f315SSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
794edd3f315SSepherosa Ziehau 		if (ip6->ip6_nxt != IPPROTO_TCP) {
795edd3f315SSepherosa Ziehau 			m_freem(m_head);
796edd3f315SSepherosa Ziehau 			return (NULL);
797edd3f315SSepherosa Ziehau 		}
798c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = sizeof(*ip6);
799edd3f315SSepherosa Ziehau 
800edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
801edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + sizeof(*ip6));
802edd3f315SSepherosa Ziehau 
803edd3f315SSepherosa Ziehau 		ip6->ip6_plen = 0;
804edd3f315SSepherosa Ziehau 		th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
805edd3f315SSepherosa Ziehau 	}
806edd3f315SSepherosa Ziehau #endif
807edd3f315SSepherosa Ziehau 	return (m_head);
808edd3f315SSepherosa Ziehau }
809cc0c6ebcSSepherosa Ziehau 
810cc0c6ebcSSepherosa Ziehau /*
811cc0c6ebcSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
812cc0c6ebcSSepherosa Ziehau  */
813cc0c6ebcSSepherosa Ziehau static __inline struct mbuf *
814c49d47daSSepherosa Ziehau hn_set_hlen(struct mbuf *m_head)
815cc0c6ebcSSepherosa Ziehau {
816cc0c6ebcSSepherosa Ziehau 	const struct ether_vlan_header *evl;
817cc0c6ebcSSepherosa Ziehau 	int ehlen;
818cc0c6ebcSSepherosa Ziehau 
819cc0c6ebcSSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
820cc0c6ebcSSepherosa Ziehau 	evl = mtod(m_head, const struct ether_vlan_header *);
821cc0c6ebcSSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
822cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
823cc0c6ebcSSepherosa Ziehau 	else
824cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
825c49d47daSSepherosa Ziehau 	m_head->m_pkthdr.l2hlen = ehlen;
826cc0c6ebcSSepherosa Ziehau 
827cc0c6ebcSSepherosa Ziehau #ifdef INET
828c49d47daSSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP_UDP)) {
829cc0c6ebcSSepherosa Ziehau 		const struct ip *ip;
830cc0c6ebcSSepherosa Ziehau 		int iphlen;
831cc0c6ebcSSepherosa Ziehau 
832cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
833cc0c6ebcSSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
834cc0c6ebcSSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
835c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = iphlen;
8362be266caSSepherosa Ziehau 
8372be266caSSepherosa Ziehau 		/*
8382be266caSSepherosa Ziehau 		 * UDP checksum offload does not work in Azure, if the
8392be266caSSepherosa Ziehau 		 * following conditions meet:
8402be266caSSepherosa Ziehau 		 * - sizeof(IP hdr + UDP hdr + payload) > 1420.
8412be266caSSepherosa Ziehau 		 * - IP_DF is not set in the IP hdr.
8422be266caSSepherosa Ziehau 		 *
8432be266caSSepherosa Ziehau 		 * Fallback to software checksum for these UDP datagrams.
8442be266caSSepherosa Ziehau 		 */
8452be266caSSepherosa Ziehau 		if ((m_head->m_pkthdr.csum_flags & CSUM_IP_UDP) &&
8462be266caSSepherosa Ziehau 		    m_head->m_pkthdr.len > hn_udpcs_fixup_mtu + ehlen &&
8472be266caSSepherosa Ziehau 		    (ntohs(ip->ip_off) & IP_DF) == 0) {
8482be266caSSepherosa Ziehau 			uint16_t off = ehlen + iphlen;
8492be266caSSepherosa Ziehau 
8502be266caSSepherosa Ziehau 			counter_u64_add(hn_udpcs_fixup, 1);
8512be266caSSepherosa Ziehau 			PULLUP_HDR(m_head, off + sizeof(struct udphdr));
8522be266caSSepherosa Ziehau 			*(uint16_t *)(m_head->m_data + off +
8532be266caSSepherosa Ziehau                             m_head->m_pkthdr.csum_data) = in_cksum_skip(
8542be266caSSepherosa Ziehau 			    m_head, m_head->m_pkthdr.len, off);
8552be266caSSepherosa Ziehau 			m_head->m_pkthdr.csum_flags &= ~CSUM_IP_UDP;
8562be266caSSepherosa Ziehau 		}
857cc0c6ebcSSepherosa Ziehau 	}
858cc0c6ebcSSepherosa Ziehau #endif
859cc0c6ebcSSepherosa Ziehau #if defined(INET6) && defined(INET)
860cc0c6ebcSSepherosa Ziehau 	else
861cc0c6ebcSSepherosa Ziehau #endif
862cc0c6ebcSSepherosa Ziehau #ifdef INET6
863cc0c6ebcSSepherosa Ziehau 	{
864cc0c6ebcSSepherosa Ziehau 		const struct ip6_hdr *ip6;
865cc0c6ebcSSepherosa Ziehau 
866cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
867cc0c6ebcSSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
868f0319886SWei Hu 		if (ip6->ip6_nxt != IPPROTO_TCP &&
869f0319886SWei Hu 		    ip6->ip6_nxt != IPPROTO_UDP) {
870c49d47daSSepherosa Ziehau 			m_freem(m_head);
871c49d47daSSepherosa Ziehau 			return (NULL);
872c49d47daSSepherosa Ziehau 		}
873c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = sizeof(*ip6);
874cc0c6ebcSSepherosa Ziehau 	}
875cc0c6ebcSSepherosa Ziehau #endif
876cc0c6ebcSSepherosa Ziehau 	return (m_head);
877cc0c6ebcSSepherosa Ziehau }
878cc0c6ebcSSepherosa Ziehau 
879c49d47daSSepherosa Ziehau /*
880c49d47daSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
881c49d47daSSepherosa Ziehau  */
882c49d47daSSepherosa Ziehau static __inline struct mbuf *
883c49d47daSSepherosa Ziehau hn_check_tcpsyn(struct mbuf *m_head, int *tcpsyn)
884c49d47daSSepherosa Ziehau {
885c49d47daSSepherosa Ziehau 	const struct tcphdr *th;
886c49d47daSSepherosa Ziehau 	int ehlen, iphlen;
887c49d47daSSepherosa Ziehau 
888c49d47daSSepherosa Ziehau 	*tcpsyn = 0;
889c49d47daSSepherosa Ziehau 	ehlen = m_head->m_pkthdr.l2hlen;
890c49d47daSSepherosa Ziehau 	iphlen = m_head->m_pkthdr.l3hlen;
891c49d47daSSepherosa Ziehau 
892c49d47daSSepherosa Ziehau 	PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
893c49d47daSSepherosa Ziehau 	th = mtodo(m_head, ehlen + iphlen);
894c49d47daSSepherosa Ziehau 	if (th->th_flags & TH_SYN)
895c49d47daSSepherosa Ziehau 		*tcpsyn = 1;
896c49d47daSSepherosa Ziehau 	return (m_head);
897c49d47daSSepherosa Ziehau }
898c49d47daSSepherosa Ziehau 
899cc0c6ebcSSepherosa Ziehau #undef PULLUP_HDR
900cc0c6ebcSSepherosa Ziehau 
901edd3f315SSepherosa Ziehau #endif	/* INET6 || INET */
902edd3f315SSepherosa Ziehau 
90315516c77SSepherosa Ziehau static int
904f1b0a43fSSepherosa Ziehau hn_set_rxfilter(struct hn_softc *sc, uint32_t filter)
905f1b0a43fSSepherosa Ziehau {
906f1b0a43fSSepherosa Ziehau 	int error = 0;
907f1b0a43fSSepherosa Ziehau 
908f1b0a43fSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
909f1b0a43fSSepherosa Ziehau 
910f1b0a43fSSepherosa Ziehau 	if (sc->hn_rx_filter != filter) {
911f1b0a43fSSepherosa Ziehau 		error = hn_rndis_set_rxfilter(sc, filter);
912f1b0a43fSSepherosa Ziehau 		if (!error)
913f1b0a43fSSepherosa Ziehau 			sc->hn_rx_filter = filter;
914f1b0a43fSSepherosa Ziehau 	}
915f1b0a43fSSepherosa Ziehau 	return (error);
916f1b0a43fSSepherosa Ziehau }
917f1b0a43fSSepherosa Ziehau 
918f1b0a43fSSepherosa Ziehau static int
919c08f7b2cSSepherosa Ziehau hn_rxfilter_config(struct hn_softc *sc)
92015516c77SSepherosa Ziehau {
92115516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
92215516c77SSepherosa Ziehau 	uint32_t filter;
92315516c77SSepherosa Ziehau 
92415516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
92515516c77SSepherosa Ziehau 
9269c6cae24SSepherosa Ziehau 	/*
9279c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, we don't know how
9289c6cae24SSepherosa Ziehau 	 * its RX filter is configured, so stick the synthetic device in
9299c6cae24SSepherosa Ziehau 	 * the promiscous mode.
9309c6cae24SSepherosa Ziehau 	 */
9319c6cae24SSepherosa Ziehau 	if ((ifp->if_flags & IFF_PROMISC) || (sc->hn_flags & HN_FLAG_RXVF)) {
93215516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
93315516c77SSepherosa Ziehau 	} else {
93415516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_DIRECTED;
93515516c77SSepherosa Ziehau 		if (ifp->if_flags & IFF_BROADCAST)
93615516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_BROADCAST;
93715516c77SSepherosa Ziehau 		/* TODO: support multicast list */
93815516c77SSepherosa Ziehau 		if ((ifp->if_flags & IFF_ALLMULTI) ||
939d7c5a620SMatt Macy 		    !CK_STAILQ_EMPTY(&ifp->if_multiaddrs))
94015516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
94115516c77SSepherosa Ziehau 	}
942f1b0a43fSSepherosa Ziehau 	return (hn_set_rxfilter(sc, filter));
94315516c77SSepherosa Ziehau }
94415516c77SSepherosa Ziehau 
945dc13fee6SSepherosa Ziehau static void
946dc13fee6SSepherosa Ziehau hn_set_txagg(struct hn_softc *sc)
947dc13fee6SSepherosa Ziehau {
948dc13fee6SSepherosa Ziehau 	uint32_t size, pkts;
949dc13fee6SSepherosa Ziehau 	int i;
950dc13fee6SSepherosa Ziehau 
951dc13fee6SSepherosa Ziehau 	/*
952dc13fee6SSepherosa Ziehau 	 * Setup aggregation size.
953dc13fee6SSepherosa Ziehau 	 */
954dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_size < 0)
955dc13fee6SSepherosa Ziehau 		size = UINT32_MAX;
956dc13fee6SSepherosa Ziehau 	else
957dc13fee6SSepherosa Ziehau 		size = sc->hn_agg_size;
958dc13fee6SSepherosa Ziehau 
959dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_size < size)
960dc13fee6SSepherosa Ziehau 		size = sc->hn_rndis_agg_size;
961dc13fee6SSepherosa Ziehau 
962a4364cfeSSepherosa Ziehau 	/* NOTE: We only aggregate packets using chimney sending buffers. */
963a4364cfeSSepherosa Ziehau 	if (size > (uint32_t)sc->hn_chim_szmax)
964a4364cfeSSepherosa Ziehau 		size = sc->hn_chim_szmax;
965a4364cfeSSepherosa Ziehau 
966dc13fee6SSepherosa Ziehau 	if (size <= 2 * HN_PKTSIZE_MIN(sc->hn_rndis_agg_align)) {
967dc13fee6SSepherosa Ziehau 		/* Disable */
968dc13fee6SSepherosa Ziehau 		size = 0;
969dc13fee6SSepherosa Ziehau 		pkts = 0;
970dc13fee6SSepherosa Ziehau 		goto done;
971dc13fee6SSepherosa Ziehau 	}
972dc13fee6SSepherosa Ziehau 
973dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'int'. */
974dc13fee6SSepherosa Ziehau 	if (size > INT_MAX)
975dc13fee6SSepherosa Ziehau 		size = INT_MAX;
976dc13fee6SSepherosa Ziehau 
977dc13fee6SSepherosa Ziehau 	/*
978dc13fee6SSepherosa Ziehau 	 * Setup aggregation packet count.
979dc13fee6SSepherosa Ziehau 	 */
980dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_pkts < 0)
981dc13fee6SSepherosa Ziehau 		pkts = UINT32_MAX;
982dc13fee6SSepherosa Ziehau 	else
983dc13fee6SSepherosa Ziehau 		pkts = sc->hn_agg_pkts;
984dc13fee6SSepherosa Ziehau 
985dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_pkts < pkts)
986dc13fee6SSepherosa Ziehau 		pkts = sc->hn_rndis_agg_pkts;
987dc13fee6SSepherosa Ziehau 
988dc13fee6SSepherosa Ziehau 	if (pkts <= 1) {
989dc13fee6SSepherosa Ziehau 		/* Disable */
990dc13fee6SSepherosa Ziehau 		size = 0;
991dc13fee6SSepherosa Ziehau 		pkts = 0;
992dc13fee6SSepherosa Ziehau 		goto done;
993dc13fee6SSepherosa Ziehau 	}
994dc13fee6SSepherosa Ziehau 
995dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
996dc13fee6SSepherosa Ziehau 	if (pkts > SHRT_MAX)
997dc13fee6SSepherosa Ziehau 		pkts = SHRT_MAX;
998dc13fee6SSepherosa Ziehau 
999dc13fee6SSepherosa Ziehau done:
1000dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
1001dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_align > SHRT_MAX) {
1002dc13fee6SSepherosa Ziehau 		/* Disable */
1003dc13fee6SSepherosa Ziehau 		size = 0;
1004dc13fee6SSepherosa Ziehau 		pkts = 0;
1005dc13fee6SSepherosa Ziehau 	}
1006dc13fee6SSepherosa Ziehau 
1007dc13fee6SSepherosa Ziehau 	if (bootverbose) {
1008dc13fee6SSepherosa Ziehau 		if_printf(sc->hn_ifp, "TX agg size %u, pkts %u, align %u\n",
1009dc13fee6SSepherosa Ziehau 		    size, pkts, sc->hn_rndis_agg_align);
1010dc13fee6SSepherosa Ziehau 	}
1011dc13fee6SSepherosa Ziehau 
1012dc13fee6SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
1013dc13fee6SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
1014dc13fee6SSepherosa Ziehau 
1015dc13fee6SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
1016dc13fee6SSepherosa Ziehau 		txr->hn_agg_szmax = size;
1017dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktmax = pkts;
1018dc13fee6SSepherosa Ziehau 		txr->hn_agg_align = sc->hn_rndis_agg_align;
1019dc13fee6SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
1020dc13fee6SSepherosa Ziehau 	}
1021dc13fee6SSepherosa Ziehau }
1022dc13fee6SSepherosa Ziehau 
102315516c77SSepherosa Ziehau static int
102415516c77SSepherosa Ziehau hn_get_txswq_depth(const struct hn_tx_ring *txr)
102515516c77SSepherosa Ziehau {
102615516c77SSepherosa Ziehau 
102715516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_cnt > 0, ("tx ring is not setup yet"));
102815516c77SSepherosa Ziehau 	if (hn_tx_swq_depth < txr->hn_txdesc_cnt)
102915516c77SSepherosa Ziehau 		return txr->hn_txdesc_cnt;
103015516c77SSepherosa Ziehau 	return hn_tx_swq_depth;
103115516c77SSepherosa Ziehau }
103215516c77SSepherosa Ziehau 
103315516c77SSepherosa Ziehau static int
103415516c77SSepherosa Ziehau hn_rss_reconfig(struct hn_softc *sc)
103515516c77SSepherosa Ziehau {
103615516c77SSepherosa Ziehau 	int error;
103715516c77SSepherosa Ziehau 
103815516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
103915516c77SSepherosa Ziehau 
104015516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
104115516c77SSepherosa Ziehau 		return (ENXIO);
104215516c77SSepherosa Ziehau 
104315516c77SSepherosa Ziehau 	/*
104415516c77SSepherosa Ziehau 	 * Disable RSS first.
104515516c77SSepherosa Ziehau 	 *
104615516c77SSepherosa Ziehau 	 * NOTE:
104715516c77SSepherosa Ziehau 	 * Direct reconfiguration by setting the UNCHG flags does
104815516c77SSepherosa Ziehau 	 * _not_ work properly.
104915516c77SSepherosa Ziehau 	 */
105015516c77SSepherosa Ziehau 	if (bootverbose)
105115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "disable RSS\n");
105215516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_DISABLE);
105315516c77SSepherosa Ziehau 	if (error) {
105415516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS disable failed\n");
105515516c77SSepherosa Ziehau 		return (error);
105615516c77SSepherosa Ziehau 	}
105715516c77SSepherosa Ziehau 
105815516c77SSepherosa Ziehau 	/*
105915516c77SSepherosa Ziehau 	 * Reenable the RSS w/ the updated RSS key or indirect
106015516c77SSepherosa Ziehau 	 * table.
106115516c77SSepherosa Ziehau 	 */
106215516c77SSepherosa Ziehau 	if (bootverbose)
106315516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "reconfig RSS\n");
106415516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
106515516c77SSepherosa Ziehau 	if (error) {
106615516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS reconfig failed\n");
106715516c77SSepherosa Ziehau 		return (error);
106815516c77SSepherosa Ziehau 	}
106915516c77SSepherosa Ziehau 	return (0);
107015516c77SSepherosa Ziehau }
107115516c77SSepherosa Ziehau 
107215516c77SSepherosa Ziehau static void
1073afd4971bSSepherosa Ziehau hn_rss_ind_fixup(struct hn_softc *sc)
107415516c77SSepherosa Ziehau {
107515516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
1076afd4971bSSepherosa Ziehau 	int i, nchan;
107715516c77SSepherosa Ziehau 
1078afd4971bSSepherosa Ziehau 	nchan = sc->hn_rx_ring_inuse;
107915516c77SSepherosa Ziehau 	KASSERT(nchan > 1, ("invalid # of channels %d", nchan));
108015516c77SSepherosa Ziehau 
108115516c77SSepherosa Ziehau 	/*
108215516c77SSepherosa Ziehau 	 * Check indirect table to make sure that all channels in it
108315516c77SSepherosa Ziehau 	 * can be used.
108415516c77SSepherosa Ziehau 	 */
108515516c77SSepherosa Ziehau 	for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
108615516c77SSepherosa Ziehau 		if (rss->rss_ind[i] >= nchan) {
108715516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp,
108815516c77SSepherosa Ziehau 			    "RSS indirect table %d fixup: %u -> %d\n",
108915516c77SSepherosa Ziehau 			    i, rss->rss_ind[i], nchan - 1);
109015516c77SSepherosa Ziehau 			rss->rss_ind[i] = nchan - 1;
109115516c77SSepherosa Ziehau 		}
109215516c77SSepherosa Ziehau 	}
109315516c77SSepherosa Ziehau }
109415516c77SSepherosa Ziehau 
109515516c77SSepherosa Ziehau static int
109615516c77SSepherosa Ziehau hn_ifmedia_upd(struct ifnet *ifp __unused)
109715516c77SSepherosa Ziehau {
109815516c77SSepherosa Ziehau 
109915516c77SSepherosa Ziehau 	return EOPNOTSUPP;
110015516c77SSepherosa Ziehau }
110115516c77SSepherosa Ziehau 
110215516c77SSepherosa Ziehau static void
110315516c77SSepherosa Ziehau hn_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
110415516c77SSepherosa Ziehau {
110515516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
110615516c77SSepherosa Ziehau 
110715516c77SSepherosa Ziehau 	ifmr->ifm_status = IFM_AVALID;
110815516c77SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
110915516c77SSepherosa Ziehau 
111015516c77SSepherosa Ziehau 	if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) {
111115516c77SSepherosa Ziehau 		ifmr->ifm_active |= IFM_NONE;
111215516c77SSepherosa Ziehau 		return;
111315516c77SSepherosa Ziehau 	}
111415516c77SSepherosa Ziehau 	ifmr->ifm_status |= IFM_ACTIVE;
111515516c77SSepherosa Ziehau 	ifmr->ifm_active |= IFM_10G_T | IFM_FDX;
111615516c77SSepherosa Ziehau }
111715516c77SSepherosa Ziehau 
11185bdfd3fdSDexuan Cui static void
1119962f0357SSepherosa Ziehau hn_rxvf_set_task(void *xarg, int pending __unused)
11205bdfd3fdSDexuan Cui {
1121962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg *arg = xarg;
11225bdfd3fdSDexuan Cui 
1123962f0357SSepherosa Ziehau 	arg->rxr->hn_rxvf_ifp = arg->vf_ifp;
11245bdfd3fdSDexuan Cui }
11255bdfd3fdSDexuan Cui 
11265bdfd3fdSDexuan Cui static void
1127962f0357SSepherosa Ziehau hn_rxvf_set(struct hn_softc *sc, struct ifnet *vf_ifp)
11285bdfd3fdSDexuan Cui {
11295bdfd3fdSDexuan Cui 	struct hn_rx_ring *rxr;
1130962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg arg;
11315bdfd3fdSDexuan Cui 	struct task task;
11325bdfd3fdSDexuan Cui 	int i;
11335bdfd3fdSDexuan Cui 
11345bdfd3fdSDexuan Cui 	HN_LOCK_ASSERT(sc);
11355bdfd3fdSDexuan Cui 
1136962f0357SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_rxvf_set_task, &arg);
11375bdfd3fdSDexuan Cui 
11385bdfd3fdSDexuan Cui 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
11395bdfd3fdSDexuan Cui 		rxr = &sc->hn_rx_ring[i];
11405bdfd3fdSDexuan Cui 
11415bdfd3fdSDexuan Cui 		if (i < sc->hn_rx_ring_inuse) {
1142962f0357SSepherosa Ziehau 			arg.rxr = rxr;
1143962f0357SSepherosa Ziehau 			arg.vf_ifp = vf_ifp;
11445bdfd3fdSDexuan Cui 			vmbus_chan_run_task(rxr->hn_chan, &task);
11455bdfd3fdSDexuan Cui 		} else {
1146962f0357SSepherosa Ziehau 			rxr->hn_rxvf_ifp = vf_ifp;
11475bdfd3fdSDexuan Cui 		}
11485bdfd3fdSDexuan Cui 	}
11495bdfd3fdSDexuan Cui }
11505bdfd3fdSDexuan Cui 
1151962f0357SSepherosa Ziehau static bool
1152499c3e17SSepherosa Ziehau hn_ismyvf(const struct hn_softc *sc, const struct ifnet *ifp)
1153499c3e17SSepherosa Ziehau {
1154499c3e17SSepherosa Ziehau 	const struct ifnet *hn_ifp;
1155499c3e17SSepherosa Ziehau 
1156499c3e17SSepherosa Ziehau 	hn_ifp = sc->hn_ifp;
1157499c3e17SSepherosa Ziehau 
1158499c3e17SSepherosa Ziehau 	if (ifp == hn_ifp)
1159499c3e17SSepherosa Ziehau 		return (false);
1160499c3e17SSepherosa Ziehau 
1161499c3e17SSepherosa Ziehau 	if (ifp->if_alloctype != IFT_ETHER)
1162499c3e17SSepherosa Ziehau 		return (false);
1163499c3e17SSepherosa Ziehau 
1164499c3e17SSepherosa Ziehau 	/* Ignore lagg/vlan interfaces */
1165499c3e17SSepherosa Ziehau 	if (strcmp(ifp->if_dname, "lagg") == 0 ||
1166499c3e17SSepherosa Ziehau 	    strcmp(ifp->if_dname, "vlan") == 0)
1167499c3e17SSepherosa Ziehau 		return (false);
1168499c3e17SSepherosa Ziehau 
1169d76fb49fSDexuan Cui 	/*
1170d76fb49fSDexuan Cui 	 * During detach events ifp->if_addr might be NULL.
1171d76fb49fSDexuan Cui 	 * Make sure the bcmp() below doesn't panic on that:
1172d76fb49fSDexuan Cui 	 */
1173d76fb49fSDexuan Cui 	if (ifp->if_addr == NULL || hn_ifp->if_addr == NULL)
1174d76fb49fSDexuan Cui 		return (false);
1175d76fb49fSDexuan Cui 
1176499c3e17SSepherosa Ziehau 	if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0)
1177499c3e17SSepherosa Ziehau 		return (false);
1178499c3e17SSepherosa Ziehau 
1179499c3e17SSepherosa Ziehau 	return (true);
1180499c3e17SSepherosa Ziehau }
1181499c3e17SSepherosa Ziehau 
11825bdfd3fdSDexuan Cui static void
1183962f0357SSepherosa Ziehau hn_rxvf_change(struct hn_softc *sc, struct ifnet *ifp, bool rxvf)
11845bdfd3fdSDexuan Cui {
11855bdfd3fdSDexuan Cui 	struct ifnet *hn_ifp;
11865bdfd3fdSDexuan Cui 
11875bdfd3fdSDexuan Cui 	HN_LOCK(sc);
11885bdfd3fdSDexuan Cui 
11895bdfd3fdSDexuan Cui 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
11905bdfd3fdSDexuan Cui 		goto out;
11915bdfd3fdSDexuan Cui 
1192499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1193499c3e17SSepherosa Ziehau 		goto out;
11945bdfd3fdSDexuan Cui 	hn_ifp = sc->hn_ifp;
11955bdfd3fdSDexuan Cui 
1196962f0357SSepherosa Ziehau 	if (rxvf) {
1197962f0357SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_RXVF)
11985bdfd3fdSDexuan Cui 			goto out;
11995bdfd3fdSDexuan Cui 
1200962f0357SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_RXVF;
12015bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
12025bdfd3fdSDexuan Cui 	} else {
1203962f0357SSepherosa Ziehau 		if (!(sc->hn_flags & HN_FLAG_RXVF))
12045bdfd3fdSDexuan Cui 			goto out;
12055bdfd3fdSDexuan Cui 
1206962f0357SSepherosa Ziehau 		sc->hn_flags &= ~HN_FLAG_RXVF;
1207499c3e17SSepherosa Ziehau 		if (hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
12085bdfd3fdSDexuan Cui 			hn_rxfilter_config(sc);
12095bdfd3fdSDexuan Cui 		else
12105bdfd3fdSDexuan Cui 			hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE);
12115bdfd3fdSDexuan Cui 	}
12125bdfd3fdSDexuan Cui 
12135bdfd3fdSDexuan Cui 	hn_nvs_set_datapath(sc,
12149c6cae24SSepherosa Ziehau 	    rxvf ? HN_NVS_DATAPATH_VF : HN_NVS_DATAPATH_SYNTH);
12155bdfd3fdSDexuan Cui 
1216962f0357SSepherosa Ziehau 	hn_rxvf_set(sc, rxvf ? ifp : NULL);
12175bdfd3fdSDexuan Cui 
1218962f0357SSepherosa Ziehau 	if (rxvf) {
1219642ec226SSepherosa Ziehau 		hn_vf_rss_fixup(sc, true);
12205bdfd3fdSDexuan Cui 		hn_suspend_mgmt(sc);
12215bdfd3fdSDexuan Cui 		sc->hn_link_flags &=
12225bdfd3fdSDexuan Cui 		    ~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG);
1223499c3e17SSepherosa Ziehau 		if_link_state_change(hn_ifp, LINK_STATE_DOWN);
12245bdfd3fdSDexuan Cui 	} else {
1225642ec226SSepherosa Ziehau 		hn_vf_rss_restore(sc);
12265bdfd3fdSDexuan Cui 		hn_resume_mgmt(sc);
12275bdfd3fdSDexuan Cui 	}
12285bdfd3fdSDexuan Cui 
1229962f0357SSepherosa Ziehau 	devctl_notify("HYPERV_NIC_VF", hn_ifp->if_xname,
1230962f0357SSepherosa Ziehau 	    rxvf ? "VF_UP" : "VF_DOWN", NULL);
123133408a34SDexuan Cui 
1232962f0357SSepherosa Ziehau 	if (bootverbose) {
1233962f0357SSepherosa Ziehau 		if_printf(hn_ifp, "datapath is switched %s %s\n",
1234962f0357SSepherosa Ziehau 		    rxvf ? "to" : "from", ifp->if_xname);
1235962f0357SSepherosa Ziehau 	}
12365bdfd3fdSDexuan Cui out:
12375bdfd3fdSDexuan Cui 	HN_UNLOCK(sc);
12385bdfd3fdSDexuan Cui }
12395bdfd3fdSDexuan Cui 
12405bdfd3fdSDexuan Cui static void
12415bdfd3fdSDexuan Cui hn_ifnet_event(void *arg, struct ifnet *ifp, int event)
12425bdfd3fdSDexuan Cui {
1243962f0357SSepherosa Ziehau 
12445bdfd3fdSDexuan Cui 	if (event != IFNET_EVENT_UP && event != IFNET_EVENT_DOWN)
12455bdfd3fdSDexuan Cui 		return;
1246962f0357SSepherosa Ziehau 	hn_rxvf_change(arg, ifp, event == IFNET_EVENT_UP);
12475bdfd3fdSDexuan Cui }
12485bdfd3fdSDexuan Cui 
12495bdfd3fdSDexuan Cui static void
12505bdfd3fdSDexuan Cui hn_ifaddr_event(void *arg, struct ifnet *ifp)
12515bdfd3fdSDexuan Cui {
1252962f0357SSepherosa Ziehau 
1253962f0357SSepherosa Ziehau 	hn_rxvf_change(arg, ifp, ifp->if_flags & IFF_UP);
12545bdfd3fdSDexuan Cui }
12555bdfd3fdSDexuan Cui 
12569c6cae24SSepherosa Ziehau static int
12579c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetcaps(struct hn_softc *sc, struct ifreq *ifr)
12589c6cae24SSepherosa Ziehau {
12599c6cae24SSepherosa Ziehau 	struct ifnet *ifp, *vf_ifp;
12609c6cae24SSepherosa Ziehau 	uint64_t tmp;
12619c6cae24SSepherosa Ziehau 	int error;
12629c6cae24SSepherosa Ziehau 
12639c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
12649c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
12659c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
12669c6cae24SSepherosa Ziehau 
12679c6cae24SSepherosa Ziehau 	/*
12689c6cae24SSepherosa Ziehau 	 * Fix up requested capabilities w/ supported capabilities,
12699c6cae24SSepherosa Ziehau 	 * since the supported capabilities could have been changed.
12709c6cae24SSepherosa Ziehau 	 */
12719c6cae24SSepherosa Ziehau 	ifr->ifr_reqcap &= ifp->if_capabilities;
12729c6cae24SSepherosa Ziehau 	/* Pass SIOCSIFCAP to VF. */
12739c6cae24SSepherosa Ziehau 	error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFCAP, (caddr_t)ifr);
12749c6cae24SSepherosa Ziehau 
12759c6cae24SSepherosa Ziehau 	/*
12769c6cae24SSepherosa Ziehau 	 * NOTE:
12779c6cae24SSepherosa Ziehau 	 * The error will be propagated to the callers, however, it
12789c6cae24SSepherosa Ziehau 	 * is _not_ useful here.
12799c6cae24SSepherosa Ziehau 	 */
12809c6cae24SSepherosa Ziehau 
12819c6cae24SSepherosa Ziehau 	/*
12829c6cae24SSepherosa Ziehau 	 * Merge VF's enabled capabilities.
12839c6cae24SSepherosa Ziehau 	 */
12849c6cae24SSepherosa Ziehau 	ifp->if_capenable = vf_ifp->if_capenable & ifp->if_capabilities;
12859c6cae24SSepherosa Ziehau 
12869c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & HN_CSUM_IP_HWASSIST(sc);
12879c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TXCSUM)
12889c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
12899c6cae24SSepherosa Ziehau 	else
12909c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
12919c6cae24SSepherosa Ziehau 
12929c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & HN_CSUM_IP6_HWASSIST(sc);
12939c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
12949c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
12959c6cae24SSepherosa Ziehau 	else
12969c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
12979c6cae24SSepherosa Ziehau 
12989c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & CSUM_IP_TSO;
12999c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TSO4)
13009c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
13019c6cae24SSepherosa Ziehau 	else
13029c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
13039c6cae24SSepherosa Ziehau 
13049c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & CSUM_IP6_TSO;
13059c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TSO6)
13069c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
13079c6cae24SSepherosa Ziehau 	else
13089c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
13099c6cae24SSepherosa Ziehau 
13109c6cae24SSepherosa Ziehau 	return (error);
13119c6cae24SSepherosa Ziehau }
13129c6cae24SSepherosa Ziehau 
13139c6cae24SSepherosa Ziehau static int
13149c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetflags(struct hn_softc *sc)
13159c6cae24SSepherosa Ziehau {
13169c6cae24SSepherosa Ziehau 	struct ifnet *vf_ifp;
13179c6cae24SSepherosa Ziehau 	struct ifreq ifr;
13189c6cae24SSepherosa Ziehau 
13199c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13209c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
13219c6cae24SSepherosa Ziehau 
13229c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
13239c6cae24SSepherosa Ziehau 	strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
13249c6cae24SSepherosa Ziehau 	ifr.ifr_flags = vf_ifp->if_flags & 0xffff;
13259c6cae24SSepherosa Ziehau 	ifr.ifr_flagshigh = vf_ifp->if_flags >> 16;
13269c6cae24SSepherosa Ziehau 	return (vf_ifp->if_ioctl(vf_ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
13279c6cae24SSepherosa Ziehau }
13289c6cae24SSepherosa Ziehau 
13299c6cae24SSepherosa Ziehau static void
13309c6cae24SSepherosa Ziehau hn_xpnt_vf_saveifflags(struct hn_softc *sc)
13319c6cae24SSepherosa Ziehau {
13329c6cae24SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
13339c6cae24SSepherosa Ziehau 	int allmulti = 0;
13349c6cae24SSepherosa Ziehau 
13359c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13369c6cae24SSepherosa Ziehau 
13379c6cae24SSepherosa Ziehau 	/* XXX vlan(4) style mcast addr maintenance */
1338d7c5a620SMatt Macy 	if (!CK_STAILQ_EMPTY(&ifp->if_multiaddrs))
13399c6cae24SSepherosa Ziehau 		allmulti = IFF_ALLMULTI;
13409c6cae24SSepherosa Ziehau 
13419c6cae24SSepherosa Ziehau 	/* Always set the VF's if_flags */
13429c6cae24SSepherosa Ziehau 	sc->hn_vf_ifp->if_flags = ifp->if_flags | allmulti;
13439c6cae24SSepherosa Ziehau }
13449c6cae24SSepherosa Ziehau 
13459c6cae24SSepherosa Ziehau static void
13469c6cae24SSepherosa Ziehau hn_xpnt_vf_input(struct ifnet *vf_ifp, struct mbuf *m)
13479c6cae24SSepherosa Ziehau {
13489c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
13499c6cae24SSepherosa Ziehau 	struct ifnet *hn_ifp = NULL;
13509c6cae24SSepherosa Ziehau 	struct mbuf *mn;
13519c6cae24SSepherosa Ziehau 
13529c6cae24SSepherosa Ziehau 	/*
13539c6cae24SSepherosa Ziehau 	 * XXX racy, if hn(4) ever detached.
13549c6cae24SSepherosa Ziehau 	 */
13559c6cae24SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
13569c6cae24SSepherosa Ziehau 	if (vf_ifp->if_index < hn_vfmap_size)
13579c6cae24SSepherosa Ziehau 		hn_ifp = hn_vfmap[vf_ifp->if_index];
13589c6cae24SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
13599c6cae24SSepherosa Ziehau 
13609c6cae24SSepherosa Ziehau 	if (hn_ifp != NULL) {
13619c6cae24SSepherosa Ziehau 		for (mn = m; mn != NULL; mn = mn->m_nextpkt) {
13623bed4e54SSepherosa Ziehau 			/*
13633bed4e54SSepherosa Ziehau 			 * Allow tapping on the VF.
13643bed4e54SSepherosa Ziehau 			 */
13659c6cae24SSepherosa Ziehau 			ETHER_BPF_MTAP(vf_ifp, mn);
13663bed4e54SSepherosa Ziehau 
13673bed4e54SSepherosa Ziehau 			/*
13683bed4e54SSepherosa Ziehau 			 * Update VF stats.
13693bed4e54SSepherosa Ziehau 			 */
13703bed4e54SSepherosa Ziehau 			if ((vf_ifp->if_capenable & IFCAP_HWSTATS) == 0) {
13713bed4e54SSepherosa Ziehau 				if_inc_counter(vf_ifp, IFCOUNTER_IBYTES,
13723bed4e54SSepherosa Ziehau 				    mn->m_pkthdr.len);
13733bed4e54SSepherosa Ziehau 			}
13743bed4e54SSepherosa Ziehau 			/*
13753bed4e54SSepherosa Ziehau 			 * XXX IFCOUNTER_IMCAST
13763bed4e54SSepherosa Ziehau 			 * This stat updating is kinda invasive, since it
13773bed4e54SSepherosa Ziehau 			 * requires two checks on the mbuf: the length check
13783bed4e54SSepherosa Ziehau 			 * and the ethernet header check.  As of this write,
13793bed4e54SSepherosa Ziehau 			 * all multicast packets go directly to hn(4), which
13803bed4e54SSepherosa Ziehau 			 * makes imcast stat updating in the VF a try in vian.
13813bed4e54SSepherosa Ziehau 			 */
13823bed4e54SSepherosa Ziehau 
13833bed4e54SSepherosa Ziehau 			/*
13843bed4e54SSepherosa Ziehau 			 * Fix up rcvif and increase hn(4)'s ipackets.
13853bed4e54SSepherosa Ziehau 			 */
13869c6cae24SSepherosa Ziehau 			mn->m_pkthdr.rcvif = hn_ifp;
13879c6cae24SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
13889c6cae24SSepherosa Ziehau 		}
13893bed4e54SSepherosa Ziehau 		/*
13903bed4e54SSepherosa Ziehau 		 * Go through hn(4)'s if_input.
13913bed4e54SSepherosa Ziehau 		 */
13929c6cae24SSepherosa Ziehau 		hn_ifp->if_input(hn_ifp, m);
13939c6cae24SSepherosa Ziehau 	} else {
13949c6cae24SSepherosa Ziehau 		/*
13959c6cae24SSepherosa Ziehau 		 * In the middle of the transition; free this
13969c6cae24SSepherosa Ziehau 		 * mbuf chain.
13979c6cae24SSepherosa Ziehau 		 */
13989c6cae24SSepherosa Ziehau 		while (m != NULL) {
13999c6cae24SSepherosa Ziehau 			mn = m->m_nextpkt;
14009c6cae24SSepherosa Ziehau 			m->m_nextpkt = NULL;
14019c6cae24SSepherosa Ziehau 			m_freem(m);
14029c6cae24SSepherosa Ziehau 			m = mn;
14039c6cae24SSepherosa Ziehau 		}
14049c6cae24SSepherosa Ziehau 	}
14059c6cae24SSepherosa Ziehau }
14069c6cae24SSepherosa Ziehau 
14079c6cae24SSepherosa Ziehau static void
14089c6cae24SSepherosa Ziehau hn_mtu_change_fixup(struct hn_softc *sc)
14099c6cae24SSepherosa Ziehau {
14109c6cae24SSepherosa Ziehau 	struct ifnet *ifp;
14119c6cae24SSepherosa Ziehau 
14129c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
14139c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
14149c6cae24SSepherosa Ziehau 
14159c6cae24SSepherosa Ziehau 	hn_set_tso_maxsize(sc, hn_tso_maxlen, ifp->if_mtu);
14169c6cae24SSepherosa Ziehau #if __FreeBSD_version >= 1100099
14179c6cae24SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_lro.lro_length_lim < HN_LRO_LENLIM_MIN(ifp))
14189c6cae24SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp));
14199c6cae24SSepherosa Ziehau #endif
14209c6cae24SSepherosa Ziehau }
14219c6cae24SSepherosa Ziehau 
1422642ec226SSepherosa Ziehau static uint32_t
1423642ec226SSepherosa Ziehau hn_rss_type_fromndis(uint32_t rss_hash)
1424642ec226SSepherosa Ziehau {
1425642ec226SSepherosa Ziehau 	uint32_t types = 0;
1426642ec226SSepherosa Ziehau 
1427642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV4)
1428642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV4;
1429642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV4)
1430642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV4;
1431642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV6)
1432642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV6;
1433642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV6_EX)
1434642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV6_EX;
1435642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV6)
1436642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV6;
1437642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV6_EX)
1438642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV6_EX;
14396f12c42eSSepherosa Ziehau 	if (rss_hash & NDIS_HASH_UDP_IPV4_X)
14406f12c42eSSepherosa Ziehau 		types |= RSS_TYPE_UDP_IPV4;
1441642ec226SSepherosa Ziehau 	return (types);
1442642ec226SSepherosa Ziehau }
1443642ec226SSepherosa Ziehau 
1444642ec226SSepherosa Ziehau static uint32_t
1445642ec226SSepherosa Ziehau hn_rss_type_tondis(uint32_t types)
1446642ec226SSepherosa Ziehau {
1447642ec226SSepherosa Ziehau 	uint32_t rss_hash = 0;
1448642ec226SSepherosa Ziehau 
14496f12c42eSSepherosa Ziehau 	KASSERT((types & (RSS_TYPE_UDP_IPV6 | RSS_TYPE_UDP_IPV6_EX)) == 0,
14506f12c42eSSepherosa Ziehau 	    ("UDP6 and UDP6EX are not supported"));
1451642ec226SSepherosa Ziehau 
1452642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV4)
1453642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV4;
1454642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV4)
1455642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV4;
1456642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV6)
1457642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV6;
1458642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV6_EX)
1459642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV6_EX;
1460642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV6)
1461642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV6;
1462642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV6_EX)
1463642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV6_EX;
14646f12c42eSSepherosa Ziehau 	if (types & RSS_TYPE_UDP_IPV4)
14656f12c42eSSepherosa Ziehau 		rss_hash |= NDIS_HASH_UDP_IPV4_X;
1466642ec226SSepherosa Ziehau 	return (rss_hash);
1467642ec226SSepherosa Ziehau }
1468642ec226SSepherosa Ziehau 
1469642ec226SSepherosa Ziehau static void
1470642ec226SSepherosa Ziehau hn_rss_mbuf_hash(struct hn_softc *sc, uint32_t mbuf_hash)
1471642ec226SSepherosa Ziehau {
1472642ec226SSepherosa Ziehau 	int i;
1473642ec226SSepherosa Ziehau 
1474642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1475642ec226SSepherosa Ziehau 
1476642ec226SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1477642ec226SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_mbuf_hash = mbuf_hash;
1478642ec226SSepherosa Ziehau }
1479642ec226SSepherosa Ziehau 
1480642ec226SSepherosa Ziehau static void
1481642ec226SSepherosa Ziehau hn_vf_rss_fixup(struct hn_softc *sc, bool reconf)
1482642ec226SSepherosa Ziehau {
1483642ec226SSepherosa Ziehau 	struct ifnet *ifp, *vf_ifp;
1484642ec226SSepherosa Ziehau 	struct ifrsshash ifrh;
1485642ec226SSepherosa Ziehau 	struct ifrsskey ifrk;
1486642ec226SSepherosa Ziehau 	int error;
1487642ec226SSepherosa Ziehau 	uint32_t my_types, diff_types, mbuf_types = 0;
1488642ec226SSepherosa Ziehau 
1489642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1490642ec226SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
1491642ec226SSepherosa Ziehau 	    ("%s: synthetic parts are not attached", sc->hn_ifp->if_xname));
1492642ec226SSepherosa Ziehau 
1493642ec226SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
1494642ec226SSepherosa Ziehau 		/* No RSS on synthetic parts; done. */
1495642ec226SSepherosa Ziehau 		return;
1496642ec226SSepherosa Ziehau 	}
1497642ec226SSepherosa Ziehau 	if ((sc->hn_rss_hcap & NDIS_HASH_FUNCTION_TOEPLITZ) == 0) {
1498642ec226SSepherosa Ziehau 		/* Synthetic parts do not support Toeplitz; done. */
1499642ec226SSepherosa Ziehau 		return;
1500642ec226SSepherosa Ziehau 	}
1501642ec226SSepherosa Ziehau 
1502642ec226SSepherosa Ziehau 	ifp = sc->hn_ifp;
1503642ec226SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
1504642ec226SSepherosa Ziehau 
1505642ec226SSepherosa Ziehau 	/*
1506642ec226SSepherosa Ziehau 	 * Extract VF's RSS key.  Only 40 bytes key for Toeplitz is
1507642ec226SSepherosa Ziehau 	 * supported.
1508642ec226SSepherosa Ziehau 	 */
1509642ec226SSepherosa Ziehau 	memset(&ifrk, 0, sizeof(ifrk));
1510642ec226SSepherosa Ziehau 	strlcpy(ifrk.ifrk_name, vf_ifp->if_xname, sizeof(ifrk.ifrk_name));
1511642ec226SSepherosa Ziehau 	error = vf_ifp->if_ioctl(vf_ifp, SIOCGIFRSSKEY, (caddr_t)&ifrk);
1512642ec226SSepherosa Ziehau 	if (error) {
1513a3b413afSHans Petter Selasky 		if_printf(ifp, "%s SIOCGIFRSSKEY failed: %d\n",
1514642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, error);
1515642ec226SSepherosa Ziehau 		goto done;
1516642ec226SSepherosa Ziehau 	}
1517642ec226SSepherosa Ziehau 	if (ifrk.ifrk_func != RSS_FUNC_TOEPLITZ) {
1518642ec226SSepherosa Ziehau 		if_printf(ifp, "%s RSS function %u is not Toeplitz\n",
1519642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, ifrk.ifrk_func);
1520642ec226SSepherosa Ziehau 		goto done;
1521642ec226SSepherosa Ziehau 	}
1522642ec226SSepherosa Ziehau 	if (ifrk.ifrk_keylen != NDIS_HASH_KEYSIZE_TOEPLITZ) {
1523642ec226SSepherosa Ziehau 		if_printf(ifp, "%s invalid RSS Toeplitz key length %d\n",
1524642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, ifrk.ifrk_keylen);
1525642ec226SSepherosa Ziehau 		goto done;
1526642ec226SSepherosa Ziehau 	}
1527642ec226SSepherosa Ziehau 
1528642ec226SSepherosa Ziehau 	/*
1529642ec226SSepherosa Ziehau 	 * Extract VF's RSS hash.  Only Toeplitz is supported.
1530642ec226SSepherosa Ziehau 	 */
1531642ec226SSepherosa Ziehau 	memset(&ifrh, 0, sizeof(ifrh));
1532642ec226SSepherosa Ziehau 	strlcpy(ifrh.ifrh_name, vf_ifp->if_xname, sizeof(ifrh.ifrh_name));
1533642ec226SSepherosa Ziehau 	error = vf_ifp->if_ioctl(vf_ifp, SIOCGIFRSSHASH, (caddr_t)&ifrh);
1534642ec226SSepherosa Ziehau 	if (error) {
1535642ec226SSepherosa Ziehau 		if_printf(ifp, "%s SIOCGRSSHASH failed: %d\n",
1536642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, error);
1537642ec226SSepherosa Ziehau 		goto done;
1538642ec226SSepherosa Ziehau 	}
1539642ec226SSepherosa Ziehau 	if (ifrh.ifrh_func != RSS_FUNC_TOEPLITZ) {
1540642ec226SSepherosa Ziehau 		if_printf(ifp, "%s RSS function %u is not Toeplitz\n",
1541642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, ifrh.ifrh_func);
1542642ec226SSepherosa Ziehau 		goto done;
1543642ec226SSepherosa Ziehau 	}
1544642ec226SSepherosa Ziehau 
1545642ec226SSepherosa Ziehau 	my_types = hn_rss_type_fromndis(sc->hn_rss_hcap);
1546642ec226SSepherosa Ziehau 	if ((ifrh.ifrh_types & my_types) == 0) {
1547642ec226SSepherosa Ziehau 		/* This disables RSS; ignore it then */
1548642ec226SSepherosa Ziehau 		if_printf(ifp, "%s intersection of RSS types failed.  "
1549642ec226SSepherosa Ziehau 		    "VF %#x, mine %#x\n", vf_ifp->if_xname,
1550642ec226SSepherosa Ziehau 		    ifrh.ifrh_types, my_types);
1551642ec226SSepherosa Ziehau 		goto done;
1552642ec226SSepherosa Ziehau 	}
1553642ec226SSepherosa Ziehau 
1554642ec226SSepherosa Ziehau 	diff_types = my_types ^ ifrh.ifrh_types;
1555642ec226SSepherosa Ziehau 	my_types &= ifrh.ifrh_types;
1556642ec226SSepherosa Ziehau 	mbuf_types = my_types;
1557642ec226SSepherosa Ziehau 
1558642ec226SSepherosa Ziehau 	/*
1559642ec226SSepherosa Ziehau 	 * Detect RSS hash value/type confliction.
1560642ec226SSepherosa Ziehau 	 *
1561642ec226SSepherosa Ziehau 	 * NOTE:
1562642ec226SSepherosa Ziehau 	 * We don't disable the hash type, but stop delivery the hash
1563642ec226SSepherosa Ziehau 	 * value/type through mbufs on RX path.
15646f12c42eSSepherosa Ziehau 	 *
15656f12c42eSSepherosa Ziehau 	 * XXX If HN_CAP_UDPHASH is set in hn_caps, then UDP 4-tuple
15666f12c42eSSepherosa Ziehau 	 * hash is delivered with type of TCP_IPV4.  This means if
15676f12c42eSSepherosa Ziehau 	 * UDP_IPV4 is enabled, then TCP_IPV4 should be forced, at
15686f12c42eSSepherosa Ziehau 	 * least to hn_mbuf_hash.  However, given that _all_ of the
15696f12c42eSSepherosa Ziehau 	 * NICs implement TCP_IPV4, this will _not_ impose any issues
15706f12c42eSSepherosa Ziehau 	 * here.
1571642ec226SSepherosa Ziehau 	 */
1572642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV4) &&
1573642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1574642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV4 | RSS_TYPE_UDP_IPV4))) {
1575642ec226SSepherosa Ziehau 		/* Conflict; disable IPV4 hash type/value delivery. */
1576642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV4 mbuf hash delivery\n");
1577642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV4;
1578642ec226SSepherosa Ziehau 	}
1579642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV6) &&
1580642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1581642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV6 | RSS_TYPE_UDP_IPV6 |
1582642ec226SSepherosa Ziehau 	      RSS_TYPE_TCP_IPV6_EX | RSS_TYPE_UDP_IPV6_EX |
1583642ec226SSepherosa Ziehau 	      RSS_TYPE_IPV6_EX))) {
1584642ec226SSepherosa Ziehau 		/* Conflict; disable IPV6 hash type/value delivery. */
1585642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV6 mbuf hash delivery\n");
1586642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV6;
1587642ec226SSepherosa Ziehau 	}
1588642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV6_EX) &&
1589642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1590642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV6 | RSS_TYPE_UDP_IPV6 |
1591642ec226SSepherosa Ziehau 	      RSS_TYPE_TCP_IPV6_EX | RSS_TYPE_UDP_IPV6_EX |
1592642ec226SSepherosa Ziehau 	      RSS_TYPE_IPV6))) {
1593642ec226SSepherosa Ziehau 		/* Conflict; disable IPV6_EX hash type/value delivery. */
1594642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV6_EX mbuf hash delivery\n");
1595642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV6_EX;
1596642ec226SSepherosa Ziehau 	}
1597642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_TCP_IPV6) &&
1598642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_TCP_IPV6_EX)) {
1599642ec226SSepherosa Ziehau 		/* Conflict; disable TCP_IPV6 hash type/value delivery. */
1600642ec226SSepherosa Ziehau 		if_printf(ifp, "disable TCP_IPV6 mbuf hash delivery\n");
1601642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_TCP_IPV6;
1602642ec226SSepherosa Ziehau 	}
1603642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_TCP_IPV6_EX) &&
1604642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_TCP_IPV6)) {
1605642ec226SSepherosa Ziehau 		/* Conflict; disable TCP_IPV6_EX hash type/value delivery. */
1606642ec226SSepherosa Ziehau 		if_printf(ifp, "disable TCP_IPV6_EX mbuf hash delivery\n");
1607642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_TCP_IPV6_EX;
1608642ec226SSepherosa Ziehau 	}
1609642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_UDP_IPV6) &&
1610642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_UDP_IPV6_EX)) {
1611642ec226SSepherosa Ziehau 		/* Conflict; disable UDP_IPV6 hash type/value delivery. */
1612642ec226SSepherosa Ziehau 		if_printf(ifp, "disable UDP_IPV6 mbuf hash delivery\n");
1613642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_UDP_IPV6;
1614642ec226SSepherosa Ziehau 	}
1615642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_UDP_IPV6_EX) &&
1616642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_UDP_IPV6)) {
1617642ec226SSepherosa Ziehau 		/* Conflict; disable UDP_IPV6_EX hash type/value delivery. */
1618642ec226SSepherosa Ziehau 		if_printf(ifp, "disable UDP_IPV6_EX mbuf hash delivery\n");
1619642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_UDP_IPV6_EX;
1620642ec226SSepherosa Ziehau 	}
1621642ec226SSepherosa Ziehau 
1622642ec226SSepherosa Ziehau 	/*
1623642ec226SSepherosa Ziehau 	 * Indirect table does not matter.
1624642ec226SSepherosa Ziehau 	 */
1625642ec226SSepherosa Ziehau 
1626642ec226SSepherosa Ziehau 	sc->hn_rss_hash = (sc->hn_rss_hcap & NDIS_HASH_FUNCTION_MASK) |
1627642ec226SSepherosa Ziehau 	    hn_rss_type_tondis(my_types);
1628642ec226SSepherosa Ziehau 	memcpy(sc->hn_rss.rss_key, ifrk.ifrk_key, sizeof(sc->hn_rss.rss_key));
1629642ec226SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
1630642ec226SSepherosa Ziehau 
1631642ec226SSepherosa Ziehau 	if (reconf) {
1632642ec226SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
1633642ec226SSepherosa Ziehau 		if (error) {
1634642ec226SSepherosa Ziehau 			/* XXX roll-back? */
1635642ec226SSepherosa Ziehau 			if_printf(ifp, "hn_rss_reconfig failed: %d\n", error);
1636642ec226SSepherosa Ziehau 			/* XXX keep going. */
1637642ec226SSepherosa Ziehau 		}
1638642ec226SSepherosa Ziehau 	}
1639642ec226SSepherosa Ziehau done:
1640642ec226SSepherosa Ziehau 	/* Hash deliverability for mbufs. */
1641642ec226SSepherosa Ziehau 	hn_rss_mbuf_hash(sc, hn_rss_type_tondis(mbuf_types));
1642642ec226SSepherosa Ziehau }
1643642ec226SSepherosa Ziehau 
1644642ec226SSepherosa Ziehau static void
1645642ec226SSepherosa Ziehau hn_vf_rss_restore(struct hn_softc *sc)
1646642ec226SSepherosa Ziehau {
1647642ec226SSepherosa Ziehau 
1648642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1649642ec226SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
1650642ec226SSepherosa Ziehau 	    ("%s: synthetic parts are not attached", sc->hn_ifp->if_xname));
1651642ec226SSepherosa Ziehau 
1652642ec226SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1)
1653642ec226SSepherosa Ziehau 		goto done;
1654642ec226SSepherosa Ziehau 
1655642ec226SSepherosa Ziehau 	/*
1656642ec226SSepherosa Ziehau 	 * Restore hash types.  Key does _not_ matter.
1657642ec226SSepherosa Ziehau 	 */
1658642ec226SSepherosa Ziehau 	if (sc->hn_rss_hash != sc->hn_rss_hcap) {
1659642ec226SSepherosa Ziehau 		int error;
1660642ec226SSepherosa Ziehau 
1661642ec226SSepherosa Ziehau 		sc->hn_rss_hash = sc->hn_rss_hcap;
1662642ec226SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
1663642ec226SSepherosa Ziehau 		if (error) {
1664642ec226SSepherosa Ziehau 			if_printf(sc->hn_ifp, "hn_rss_reconfig failed: %d\n",
1665642ec226SSepherosa Ziehau 			    error);
1666642ec226SSepherosa Ziehau 			/* XXX keep going. */
1667642ec226SSepherosa Ziehau 		}
1668642ec226SSepherosa Ziehau 	}
1669642ec226SSepherosa Ziehau done:
1670642ec226SSepherosa Ziehau 	/* Hash deliverability for mbufs. */
1671642ec226SSepherosa Ziehau 	hn_rss_mbuf_hash(sc, NDIS_HASH_ALL);
1672642ec226SSepherosa Ziehau }
1673642ec226SSepherosa Ziehau 
16749c6cae24SSepherosa Ziehau static void
16759c6cae24SSepherosa Ziehau hn_xpnt_vf_setready(struct hn_softc *sc)
16769c6cae24SSepherosa Ziehau {
16779c6cae24SSepherosa Ziehau 	struct ifnet *ifp, *vf_ifp;
16789c6cae24SSepherosa Ziehau 	struct ifreq ifr;
16799c6cae24SSepherosa Ziehau 
16809c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
16819c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
16829c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
16839c6cae24SSepherosa Ziehau 
16849c6cae24SSepherosa Ziehau 	/*
16859c6cae24SSepherosa Ziehau 	 * Mark the VF ready.
16869c6cae24SSepherosa Ziehau 	 */
16879c6cae24SSepherosa Ziehau 	sc->hn_vf_rdytick = 0;
16889c6cae24SSepherosa Ziehau 
16899c6cae24SSepherosa Ziehau 	/*
16909c6cae24SSepherosa Ziehau 	 * Save information for restoration.
16919c6cae24SSepherosa Ziehau 	 */
16929c6cae24SSepherosa Ziehau 	sc->hn_saved_caps = ifp->if_capabilities;
16939c6cae24SSepherosa Ziehau 	sc->hn_saved_tsomax = ifp->if_hw_tsomax;
16949c6cae24SSepherosa Ziehau 	sc->hn_saved_tsosegcnt = ifp->if_hw_tsomaxsegcount;
16959c6cae24SSepherosa Ziehau 	sc->hn_saved_tsosegsz = ifp->if_hw_tsomaxsegsize;
16969c6cae24SSepherosa Ziehau 
16979c6cae24SSepherosa Ziehau 	/*
16989c6cae24SSepherosa Ziehau 	 * Intersect supported/enabled capabilities.
16999c6cae24SSepherosa Ziehau 	 *
17009c6cae24SSepherosa Ziehau 	 * NOTE:
17019c6cae24SSepherosa Ziehau 	 * if_hwassist is not changed here.
17029c6cae24SSepherosa Ziehau 	 */
17039c6cae24SSepherosa Ziehau 	ifp->if_capabilities &= vf_ifp->if_capabilities;
17049c6cae24SSepherosa Ziehau 	ifp->if_capenable &= ifp->if_capabilities;
17059c6cae24SSepherosa Ziehau 
17069c6cae24SSepherosa Ziehau 	/*
17079c6cae24SSepherosa Ziehau 	 * Fix TSO settings.
17089c6cae24SSepherosa Ziehau 	 */
17099c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomax > vf_ifp->if_hw_tsomax)
17109c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomax = vf_ifp->if_hw_tsomax;
17119c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomaxsegcount > vf_ifp->if_hw_tsomaxsegcount)
17129c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomaxsegcount = vf_ifp->if_hw_tsomaxsegcount;
17139c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomaxsegsize > vf_ifp->if_hw_tsomaxsegsize)
17149c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomaxsegsize = vf_ifp->if_hw_tsomaxsegsize;
17159c6cae24SSepherosa Ziehau 
17169c6cae24SSepherosa Ziehau 	/*
17179c6cae24SSepherosa Ziehau 	 * Change VF's enabled capabilities.
17189c6cae24SSepherosa Ziehau 	 */
17199c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
17209c6cae24SSepherosa Ziehau 	strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
17219c6cae24SSepherosa Ziehau 	ifr.ifr_reqcap = ifp->if_capenable;
17229c6cae24SSepherosa Ziehau 	hn_xpnt_vf_iocsetcaps(sc, &ifr);
17239c6cae24SSepherosa Ziehau 
17249c6cae24SSepherosa Ziehau 	if (ifp->if_mtu != ETHERMTU) {
17259c6cae24SSepherosa Ziehau 		int error;
17269c6cae24SSepherosa Ziehau 
17279c6cae24SSepherosa Ziehau 		/*
17289c6cae24SSepherosa Ziehau 		 * Change VF's MTU.
17299c6cae24SSepherosa Ziehau 		 */
17309c6cae24SSepherosa Ziehau 		memset(&ifr, 0, sizeof(ifr));
17319c6cae24SSepherosa Ziehau 		strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
17329c6cae24SSepherosa Ziehau 		ifr.ifr_mtu = ifp->if_mtu;
17339c6cae24SSepherosa Ziehau 		error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFMTU, (caddr_t)&ifr);
17349c6cae24SSepherosa Ziehau 		if (error) {
17359c6cae24SSepherosa Ziehau 			if_printf(ifp, "%s SIOCSIFMTU %u failed\n",
17369c6cae24SSepherosa Ziehau 			    vf_ifp->if_xname, ifp->if_mtu);
17379c6cae24SSepherosa Ziehau 			if (ifp->if_mtu > ETHERMTU) {
17389c6cae24SSepherosa Ziehau 				if_printf(ifp, "change MTU to %d\n", ETHERMTU);
17399c6cae24SSepherosa Ziehau 
17409c6cae24SSepherosa Ziehau 				/*
17419c6cae24SSepherosa Ziehau 				 * XXX
17429c6cae24SSepherosa Ziehau 				 * No need to adjust the synthetic parts' MTU;
17439c6cae24SSepherosa Ziehau 				 * failure of the adjustment will cause us
17449c6cae24SSepherosa Ziehau 				 * infinite headache.
17459c6cae24SSepherosa Ziehau 				 */
17469c6cae24SSepherosa Ziehau 				ifp->if_mtu = ETHERMTU;
17479c6cae24SSepherosa Ziehau 				hn_mtu_change_fixup(sc);
17489c6cae24SSepherosa Ziehau 			}
17499c6cae24SSepherosa Ziehau 		}
17509c6cae24SSepherosa Ziehau 	}
17519c6cae24SSepherosa Ziehau }
17529c6cae24SSepherosa Ziehau 
17539c6cae24SSepherosa Ziehau static bool
17549c6cae24SSepherosa Ziehau hn_xpnt_vf_isready(struct hn_softc *sc)
17559c6cae24SSepherosa Ziehau {
17569c6cae24SSepherosa Ziehau 
17579c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
17589c6cae24SSepherosa Ziehau 
17599c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf || sc->hn_vf_ifp == NULL)
17609c6cae24SSepherosa Ziehau 		return (false);
17619c6cae24SSepherosa Ziehau 
17629c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick == 0)
17639c6cae24SSepherosa Ziehau 		return (true);
17649c6cae24SSepherosa Ziehau 
17659c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick > ticks)
17669c6cae24SSepherosa Ziehau 		return (false);
17679c6cae24SSepherosa Ziehau 
17689c6cae24SSepherosa Ziehau 	/* Mark VF as ready. */
17699c6cae24SSepherosa Ziehau 	hn_xpnt_vf_setready(sc);
17709c6cae24SSepherosa Ziehau 	return (true);
17719c6cae24SSepherosa Ziehau }
17729c6cae24SSepherosa Ziehau 
17739c6cae24SSepherosa Ziehau static void
1774a97fff19SSepherosa Ziehau hn_xpnt_vf_setenable(struct hn_softc *sc)
1775a97fff19SSepherosa Ziehau {
1776a97fff19SSepherosa Ziehau 	int i;
1777a97fff19SSepherosa Ziehau 
1778a97fff19SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1779a97fff19SSepherosa Ziehau 
1780a97fff19SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
1781a97fff19SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
1782a97fff19SSepherosa Ziehau 	sc->hn_xvf_flags |= HN_XVFFLAG_ENABLED;
1783a97fff19SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1784a97fff19SSepherosa Ziehau 
1785a97fff19SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1786a97fff19SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_XPNT_VF;
1787a97fff19SSepherosa Ziehau }
1788a97fff19SSepherosa Ziehau 
1789a97fff19SSepherosa Ziehau static void
1790a97fff19SSepherosa Ziehau hn_xpnt_vf_setdisable(struct hn_softc *sc, bool clear_vf)
1791a97fff19SSepherosa Ziehau {
1792a97fff19SSepherosa Ziehau 	int i;
1793a97fff19SSepherosa Ziehau 
1794a97fff19SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1795a97fff19SSepherosa Ziehau 
1796a97fff19SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
1797a97fff19SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
1798a97fff19SSepherosa Ziehau 	sc->hn_xvf_flags &= ~HN_XVFFLAG_ENABLED;
1799a97fff19SSepherosa Ziehau 	if (clear_vf)
1800a97fff19SSepherosa Ziehau 		sc->hn_vf_ifp = NULL;
1801a97fff19SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1802a97fff19SSepherosa Ziehau 
1803a97fff19SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1804a97fff19SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_rx_flags &= ~HN_RX_FLAG_XPNT_VF;
1805a97fff19SSepherosa Ziehau }
1806a97fff19SSepherosa Ziehau 
1807a97fff19SSepherosa Ziehau static void
18089c6cae24SSepherosa Ziehau hn_xpnt_vf_init(struct hn_softc *sc)
18099c6cae24SSepherosa Ziehau {
18109c6cae24SSepherosa Ziehau 	int error;
18119c6cae24SSepherosa Ziehau 
18129c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
18139c6cae24SSepherosa Ziehau 
18149c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
18159c6cae24SSepherosa Ziehau 	    ("%s: transparent VF was enabled", sc->hn_ifp->if_xname));
18169c6cae24SSepherosa Ziehau 
18179c6cae24SSepherosa Ziehau 	if (bootverbose) {
18189c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "try bringing up %s\n",
18199c6cae24SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname);
18209c6cae24SSepherosa Ziehau 	}
18219c6cae24SSepherosa Ziehau 
18229c6cae24SSepherosa Ziehau 	/*
18239c6cae24SSepherosa Ziehau 	 * Bring the VF up.
18249c6cae24SSepherosa Ziehau 	 */
18259c6cae24SSepherosa Ziehau 	hn_xpnt_vf_saveifflags(sc);
18269c6cae24SSepherosa Ziehau 	sc->hn_vf_ifp->if_flags |= IFF_UP;
18279c6cae24SSepherosa Ziehau 	error = hn_xpnt_vf_iocsetflags(sc);
18289c6cae24SSepherosa Ziehau 	if (error) {
18299c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "bringing up %s failed: %d\n",
18309c6cae24SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname, error);
18319c6cae24SSepherosa Ziehau 		return;
18329c6cae24SSepherosa Ziehau 	}
18339c6cae24SSepherosa Ziehau 
18349c6cae24SSepherosa Ziehau 	/*
18359c6cae24SSepherosa Ziehau 	 * NOTE:
18369c6cae24SSepherosa Ziehau 	 * Datapath setting must happen _after_ bringing the VF up.
18379c6cae24SSepherosa Ziehau 	 */
18389c6cae24SSepherosa Ziehau 	hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
18399c6cae24SSepherosa Ziehau 
1840642ec226SSepherosa Ziehau 	/*
1841642ec226SSepherosa Ziehau 	 * NOTE:
1842642ec226SSepherosa Ziehau 	 * Fixup RSS related bits _after_ the VF is brought up, since
1843642ec226SSepherosa Ziehau 	 * many VFs generate RSS key during it's initialization.
1844642ec226SSepherosa Ziehau 	 */
1845642ec226SSepherosa Ziehau 	hn_vf_rss_fixup(sc, true);
1846642ec226SSepherosa Ziehau 
1847a97fff19SSepherosa Ziehau 	/* Mark transparent mode VF as enabled. */
1848a97fff19SSepherosa Ziehau 	hn_xpnt_vf_setenable(sc);
18499c6cae24SSepherosa Ziehau }
18509c6cae24SSepherosa Ziehau 
18519c6cae24SSepherosa Ziehau static void
18529c6cae24SSepherosa Ziehau hn_xpnt_vf_init_taskfunc(void *xsc, int pending __unused)
18539c6cae24SSepherosa Ziehau {
18549c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
18559c6cae24SSepherosa Ziehau 
18569c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
18579c6cae24SSepherosa Ziehau 
18589c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
18599c6cae24SSepherosa Ziehau 		goto done;
18609c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
18619c6cae24SSepherosa Ziehau 		goto done;
18629c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
18639c6cae24SSepherosa Ziehau 		goto done;
18649c6cae24SSepherosa Ziehau 
18659c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick != 0) {
18669c6cae24SSepherosa Ziehau 		/* Mark VF as ready. */
18679c6cae24SSepherosa Ziehau 		hn_xpnt_vf_setready(sc);
18689c6cae24SSepherosa Ziehau 	}
18699c6cae24SSepherosa Ziehau 
18709c6cae24SSepherosa Ziehau 	if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) {
18719c6cae24SSepherosa Ziehau 		/*
18729c6cae24SSepherosa Ziehau 		 * Delayed VF initialization.
18739c6cae24SSepherosa Ziehau 		 */
18749c6cae24SSepherosa Ziehau 		if (bootverbose) {
18759c6cae24SSepherosa Ziehau 			if_printf(sc->hn_ifp, "delayed initialize %s\n",
18769c6cae24SSepherosa Ziehau 			    sc->hn_vf_ifp->if_xname);
18779c6cae24SSepherosa Ziehau 		}
18789c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
18799c6cae24SSepherosa Ziehau 	}
18809c6cae24SSepherosa Ziehau done:
18819c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
18829c6cae24SSepherosa Ziehau }
18839c6cae24SSepherosa Ziehau 
1884499c3e17SSepherosa Ziehau static void
1885499c3e17SSepherosa Ziehau hn_ifnet_attevent(void *xsc, struct ifnet *ifp)
1886499c3e17SSepherosa Ziehau {
1887499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1888499c3e17SSepherosa Ziehau 
1889499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1890499c3e17SSepherosa Ziehau 
1891499c3e17SSepherosa Ziehau 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
1892499c3e17SSepherosa Ziehau 		goto done;
1893499c3e17SSepherosa Ziehau 
1894499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1895499c3e17SSepherosa Ziehau 		goto done;
1896499c3e17SSepherosa Ziehau 
1897499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp != NULL) {
1898499c3e17SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s was attached as VF\n",
1899499c3e17SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname);
1900499c3e17SSepherosa Ziehau 		goto done;
1901499c3e17SSepherosa Ziehau 	}
1902499c3e17SSepherosa Ziehau 
19039c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && ifp->if_start != NULL) {
19049c6cae24SSepherosa Ziehau 		/*
19059c6cae24SSepherosa Ziehau 		 * ifnet.if_start is _not_ supported by transparent
19069c6cae24SSepherosa Ziehau 		 * mode VF; mainly due to the IFF_DRV_OACTIVE flag.
19079c6cae24SSepherosa Ziehau 		 */
19089c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s uses if_start, which is unsupported "
19099c6cae24SSepherosa Ziehau 		    "in transparent VF mode.\n", ifp->if_xname);
19109c6cae24SSepherosa Ziehau 		goto done;
19119c6cae24SSepherosa Ziehau 	}
19129c6cae24SSepherosa Ziehau 
1913499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
1914499c3e17SSepherosa Ziehau 
1915499c3e17SSepherosa Ziehau 	if (ifp->if_index >= hn_vfmap_size) {
1916499c3e17SSepherosa Ziehau 		struct ifnet **newmap;
1917499c3e17SSepherosa Ziehau 		int newsize;
1918499c3e17SSepherosa Ziehau 
1919499c3e17SSepherosa Ziehau 		newsize = ifp->if_index + HN_VFMAP_SIZE_DEF;
1920499c3e17SSepherosa Ziehau 		newmap = malloc(sizeof(struct ifnet *) * newsize, M_DEVBUF,
1921499c3e17SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
1922499c3e17SSepherosa Ziehau 
1923499c3e17SSepherosa Ziehau 		memcpy(newmap, hn_vfmap,
1924499c3e17SSepherosa Ziehau 		    sizeof(struct ifnet *) * hn_vfmap_size);
1925499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
1926499c3e17SSepherosa Ziehau 		hn_vfmap = newmap;
1927499c3e17SSepherosa Ziehau 		hn_vfmap_size = newsize;
1928499c3e17SSepherosa Ziehau 	}
1929499c3e17SSepherosa Ziehau 	KASSERT(hn_vfmap[ifp->if_index] == NULL,
1930499c3e17SSepherosa Ziehau 	    ("%s: ifindex %d was mapped to %s",
1931499c3e17SSepherosa Ziehau 	     ifp->if_xname, ifp->if_index, hn_vfmap[ifp->if_index]->if_xname));
1932499c3e17SSepherosa Ziehau 	hn_vfmap[ifp->if_index] = sc->hn_ifp;
1933499c3e17SSepherosa Ziehau 
1934499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
1935499c3e17SSepherosa Ziehau 
19369c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
19379c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
19389c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
19399c6cae24SSepherosa Ziehau 	    ("%s: transparent VF was enabled", sc->hn_ifp->if_xname));
1940499c3e17SSepherosa Ziehau 	sc->hn_vf_ifp = ifp;
19419c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
19429c6cae24SSepherosa Ziehau 
19439c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
19449c6cae24SSepherosa Ziehau 		int wait_ticks;
19459c6cae24SSepherosa Ziehau 
19469c6cae24SSepherosa Ziehau 		/*
19479c6cae24SSepherosa Ziehau 		 * Install if_input for vf_ifp, which does vf_ifp -> hn_ifp.
19489c6cae24SSepherosa Ziehau 		 * Save vf_ifp's current if_input for later restoration.
19499c6cae24SSepherosa Ziehau 		 */
19509c6cae24SSepherosa Ziehau 		sc->hn_vf_input = ifp->if_input;
19519c6cae24SSepherosa Ziehau 		ifp->if_input = hn_xpnt_vf_input;
19529c6cae24SSepherosa Ziehau 
19539c6cae24SSepherosa Ziehau 		/*
19549c6cae24SSepherosa Ziehau 		 * Stop link status management; use the VF's.
19559c6cae24SSepherosa Ziehau 		 */
19569c6cae24SSepherosa Ziehau 		hn_suspend_mgmt(sc);
19579c6cae24SSepherosa Ziehau 
19589c6cae24SSepherosa Ziehau 		/*
19599c6cae24SSepherosa Ziehau 		 * Give VF sometime to complete its attach routing.
19609c6cae24SSepherosa Ziehau 		 */
19619c6cae24SSepherosa Ziehau 		wait_ticks = hn_xpnt_vf_attwait * hz;
19629c6cae24SSepherosa Ziehau 		sc->hn_vf_rdytick = ticks + wait_ticks;
19639c6cae24SSepherosa Ziehau 
19649c6cae24SSepherosa Ziehau 		taskqueue_enqueue_timeout(sc->hn_vf_taskq, &sc->hn_vf_init,
19659c6cae24SSepherosa Ziehau 		    wait_ticks);
19669c6cae24SSepherosa Ziehau 	}
1967499c3e17SSepherosa Ziehau done:
1968499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
1969499c3e17SSepherosa Ziehau }
1970499c3e17SSepherosa Ziehau 
1971499c3e17SSepherosa Ziehau static void
1972499c3e17SSepherosa Ziehau hn_ifnet_detevent(void *xsc, struct ifnet *ifp)
1973499c3e17SSepherosa Ziehau {
1974499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1975499c3e17SSepherosa Ziehau 
1976499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1977499c3e17SSepherosa Ziehau 
1978499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
1979499c3e17SSepherosa Ziehau 		goto done;
1980499c3e17SSepherosa Ziehau 
1981499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1982499c3e17SSepherosa Ziehau 		goto done;
1983499c3e17SSepherosa Ziehau 
19849c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
19859c6cae24SSepherosa Ziehau 		/*
19869c6cae24SSepherosa Ziehau 		 * Make sure that the delayed initialization is not running.
19879c6cae24SSepherosa Ziehau 		 *
19889c6cae24SSepherosa Ziehau 		 * NOTE:
19899c6cae24SSepherosa Ziehau 		 * - This lock _must_ be released, since the hn_vf_init task
19909c6cae24SSepherosa Ziehau 		 *   will try holding this lock.
19919c6cae24SSepherosa Ziehau 		 * - It is safe to release this lock here, since the
19929c6cae24SSepherosa Ziehau 		 *   hn_ifnet_attevent() is interlocked by the hn_vf_ifp.
19939c6cae24SSepherosa Ziehau 		 *
19949c6cae24SSepherosa Ziehau 		 * XXX racy, if hn(4) ever detached.
19959c6cae24SSepherosa Ziehau 		 */
19969c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
19979c6cae24SSepherosa Ziehau 		taskqueue_drain_timeout(sc->hn_vf_taskq, &sc->hn_vf_init);
19989c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
19999c6cae24SSepherosa Ziehau 
20009c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_input != NULL, ("%s VF input is not saved",
20019c6cae24SSepherosa Ziehau 		    sc->hn_ifp->if_xname));
20029c6cae24SSepherosa Ziehau 		ifp->if_input = sc->hn_vf_input;
20039c6cae24SSepherosa Ziehau 		sc->hn_vf_input = NULL;
20049c6cae24SSepherosa Ziehau 
2005642ec226SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) &&
2006642ec226SSepherosa Ziehau 		    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED))
20079c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
20089c6cae24SSepherosa Ziehau 
20099c6cae24SSepherosa Ziehau 		if (sc->hn_vf_rdytick == 0) {
20109c6cae24SSepherosa Ziehau 			/*
20119c6cae24SSepherosa Ziehau 			 * The VF was ready; restore some settings.
20129c6cae24SSepherosa Ziehau 			 */
20139c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_capabilities = sc->hn_saved_caps;
20149c6cae24SSepherosa Ziehau 			/*
20159c6cae24SSepherosa Ziehau 			 * NOTE:
20169c6cae24SSepherosa Ziehau 			 * There is _no_ need to fixup if_capenable and
20179c6cae24SSepherosa Ziehau 			 * if_hwassist, since the if_capabilities before
20189c6cae24SSepherosa Ziehau 			 * restoration was an intersection of the VF's
20199c6cae24SSepherosa Ziehau 			 * if_capabilites and the synthetic device's
20209c6cae24SSepherosa Ziehau 			 * if_capabilites.
20219c6cae24SSepherosa Ziehau 			 */
20229c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomax = sc->hn_saved_tsomax;
20239c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomaxsegcount =
20249c6cae24SSepherosa Ziehau 			    sc->hn_saved_tsosegcnt;
20259c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomaxsegsize = sc->hn_saved_tsosegsz;
20269c6cae24SSepherosa Ziehau 		}
20279c6cae24SSepherosa Ziehau 
2028642ec226SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
2029642ec226SSepherosa Ziehau 			/*
2030642ec226SSepherosa Ziehau 			 * Restore RSS settings.
2031642ec226SSepherosa Ziehau 			 */
2032642ec226SSepherosa Ziehau 			hn_vf_rss_restore(sc);
2033642ec226SSepherosa Ziehau 
20349c6cae24SSepherosa Ziehau 			/*
20359c6cae24SSepherosa Ziehau 			 * Resume link status management, which was suspended
20369c6cae24SSepherosa Ziehau 			 * by hn_ifnet_attevent().
20379c6cae24SSepherosa Ziehau 			 */
20389c6cae24SSepherosa Ziehau 			hn_resume_mgmt(sc);
20399c6cae24SSepherosa Ziehau 		}
2040642ec226SSepherosa Ziehau 	}
20419c6cae24SSepherosa Ziehau 
2042a97fff19SSepherosa Ziehau 	/* Mark transparent mode VF as disabled. */
2043a97fff19SSepherosa Ziehau 	hn_xpnt_vf_setdisable(sc, true /* clear hn_vf_ifp */);
2044499c3e17SSepherosa Ziehau 
2045499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
2046499c3e17SSepherosa Ziehau 
2047499c3e17SSepherosa Ziehau 	KASSERT(ifp->if_index < hn_vfmap_size,
2048499c3e17SSepherosa Ziehau 	    ("ifindex %d, vfmapsize %d", ifp->if_index, hn_vfmap_size));
2049499c3e17SSepherosa Ziehau 	if (hn_vfmap[ifp->if_index] != NULL) {
2050499c3e17SSepherosa Ziehau 		KASSERT(hn_vfmap[ifp->if_index] == sc->hn_ifp,
2051499c3e17SSepherosa Ziehau 		    ("%s: ifindex %d was mapped to %s",
2052499c3e17SSepherosa Ziehau 		     ifp->if_xname, ifp->if_index,
2053499c3e17SSepherosa Ziehau 		     hn_vfmap[ifp->if_index]->if_xname));
2054499c3e17SSepherosa Ziehau 		hn_vfmap[ifp->if_index] = NULL;
2055499c3e17SSepherosa Ziehau 	}
2056499c3e17SSepherosa Ziehau 
2057499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
2058499c3e17SSepherosa Ziehau done:
2059499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
2060499c3e17SSepherosa Ziehau }
2061499c3e17SSepherosa Ziehau 
20629c6cae24SSepherosa Ziehau static void
20639c6cae24SSepherosa Ziehau hn_ifnet_lnkevent(void *xsc, struct ifnet *ifp, int link_state)
20649c6cae24SSepherosa Ziehau {
20659c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
20669c6cae24SSepherosa Ziehau 
20679c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == ifp)
20689c6cae24SSepherosa Ziehau 		if_link_state_change(sc->hn_ifp, link_state);
20699c6cae24SSepherosa Ziehau }
20709c6cae24SSepherosa Ziehau 
207115516c77SSepherosa Ziehau static int
207215516c77SSepherosa Ziehau hn_probe(device_t dev)
207315516c77SSepherosa Ziehau {
207415516c77SSepherosa Ziehau 
2075c2d50b26SSepherosa Ziehau 	if (VMBUS_PROBE_GUID(device_get_parent(dev), dev, &hn_guid) == 0) {
207615516c77SSepherosa Ziehau 		device_set_desc(dev, "Hyper-V Network Interface");
207715516c77SSepherosa Ziehau 		return BUS_PROBE_DEFAULT;
207815516c77SSepherosa Ziehau 	}
207915516c77SSepherosa Ziehau 	return ENXIO;
208015516c77SSepherosa Ziehau }
208115516c77SSepherosa Ziehau 
208215516c77SSepherosa Ziehau static int
208315516c77SSepherosa Ziehau hn_attach(device_t dev)
208415516c77SSepherosa Ziehau {
208515516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
208615516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
208715516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
208815516c77SSepherosa Ziehau 	uint8_t eaddr[ETHER_ADDR_LEN];
208915516c77SSepherosa Ziehau 	struct ifnet *ifp = NULL;
209015516c77SSepherosa Ziehau 	int error, ring_cnt, tx_ring_cnt;
2091eb2fe044SSepherosa Ziehau 	uint32_t mtu;
209215516c77SSepherosa Ziehau 
209315516c77SSepherosa Ziehau 	sc->hn_dev = dev;
209415516c77SSepherosa Ziehau 	sc->hn_prichan = vmbus_get_channel(dev);
209515516c77SSepherosa Ziehau 	HN_LOCK_INIT(sc);
20969c6cae24SSepherosa Ziehau 	rm_init(&sc->hn_vf_lock, "hnvf");
20979c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_xpnt_vf_accbpf)
20989c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
209915516c77SSepherosa Ziehau 
210015516c77SSepherosa Ziehau 	/*
2101dc13fee6SSepherosa Ziehau 	 * Initialize these tunables once.
2102dc13fee6SSepherosa Ziehau 	 */
2103dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = hn_tx_agg_size;
2104dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = hn_tx_agg_pkts;
2105dc13fee6SSepherosa Ziehau 
2106dc13fee6SSepherosa Ziehau 	/*
210715516c77SSepherosa Ziehau 	 * Setup taskqueue for transmission.
210815516c77SSepherosa Ziehau 	 */
21090e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_INDEP) {
2110fdd0222aSSepherosa Ziehau 		int i;
2111fdd0222aSSepherosa Ziehau 
2112fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs =
2113fdd0222aSSepherosa Ziehau 		    malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
2114fdd0222aSSepherosa Ziehau 		    M_DEVBUF, M_WAITOK);
2115fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i) {
2116fdd0222aSSepherosa Ziehau 			sc->hn_tx_taskqs[i] = taskqueue_create("hn_tx",
2117fdd0222aSSepherosa Ziehau 			    M_WAITOK, taskqueue_thread_enqueue,
2118fdd0222aSSepherosa Ziehau 			    &sc->hn_tx_taskqs[i]);
2119fdd0222aSSepherosa Ziehau 			taskqueue_start_threads(&sc->hn_tx_taskqs[i], 1, PI_NET,
2120fdd0222aSSepherosa Ziehau 			    "%s tx%d", device_get_nameunit(dev), i);
2121fdd0222aSSepherosa Ziehau 		}
21220e11868dSSepherosa Ziehau 	} else if (hn_tx_taskq_mode == HN_TX_TASKQ_M_GLOBAL) {
2123fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs = hn_tx_taskque;
212415516c77SSepherosa Ziehau 	}
212515516c77SSepherosa Ziehau 
212615516c77SSepherosa Ziehau 	/*
212715516c77SSepherosa Ziehau 	 * Setup taskqueue for mangement tasks, e.g. link status.
212815516c77SSepherosa Ziehau 	 */
212915516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq0 = taskqueue_create("hn_mgmt", M_WAITOK,
213015516c77SSepherosa Ziehau 	    taskqueue_thread_enqueue, &sc->hn_mgmt_taskq0);
213115516c77SSepherosa Ziehau 	taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
213215516c77SSepherosa Ziehau 	    device_get_nameunit(dev));
213315516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc);
213415516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_netchg_init, 0, hn_netchg_init_taskfunc, sc);
213515516c77SSepherosa Ziehau 	TIMEOUT_TASK_INIT(sc->hn_mgmt_taskq0, &sc->hn_netchg_status, 0,
213615516c77SSepherosa Ziehau 	    hn_netchg_status_taskfunc, sc);
213715516c77SSepherosa Ziehau 
21389c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
21399c6cae24SSepherosa Ziehau 		/*
21409c6cae24SSepherosa Ziehau 		 * Setup taskqueue for VF tasks, e.g. delayed VF bringing up.
21419c6cae24SSepherosa Ziehau 		 */
21429c6cae24SSepherosa Ziehau 		sc->hn_vf_taskq = taskqueue_create("hn_vf", M_WAITOK,
21439c6cae24SSepherosa Ziehau 		    taskqueue_thread_enqueue, &sc->hn_vf_taskq);
21449c6cae24SSepherosa Ziehau 		taskqueue_start_threads(&sc->hn_vf_taskq, 1, PI_NET, "%s vf",
21459c6cae24SSepherosa Ziehau 		    device_get_nameunit(dev));
21469c6cae24SSepherosa Ziehau 		TIMEOUT_TASK_INIT(sc->hn_vf_taskq, &sc->hn_vf_init, 0,
21479c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_init_taskfunc, sc);
21489c6cae24SSepherosa Ziehau 	}
21499c6cae24SSepherosa Ziehau 
215015516c77SSepherosa Ziehau 	/*
215115516c77SSepherosa Ziehau 	 * Allocate ifnet and setup its name earlier, so that if_printf
215215516c77SSepherosa Ziehau 	 * can be used by functions, which will be called after
215315516c77SSepherosa Ziehau 	 * ether_ifattach().
215415516c77SSepherosa Ziehau 	 */
215515516c77SSepherosa Ziehau 	ifp = sc->hn_ifp = if_alloc(IFT_ETHER);
215615516c77SSepherosa Ziehau 	ifp->if_softc = sc;
215715516c77SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
215815516c77SSepherosa Ziehau 
215915516c77SSepherosa Ziehau 	/*
216015516c77SSepherosa Ziehau 	 * Initialize ifmedia earlier so that it can be unconditionally
216115516c77SSepherosa Ziehau 	 * destroyed, if error happened later on.
216215516c77SSepherosa Ziehau 	 */
216315516c77SSepherosa Ziehau 	ifmedia_init(&sc->hn_media, 0, hn_ifmedia_upd, hn_ifmedia_sts);
216415516c77SSepherosa Ziehau 
216515516c77SSepherosa Ziehau 	/*
216615516c77SSepherosa Ziehau 	 * Figure out the # of RX rings (ring_cnt) and the # of TX rings
216715516c77SSepherosa Ziehau 	 * to use (tx_ring_cnt).
216815516c77SSepherosa Ziehau 	 *
216915516c77SSepherosa Ziehau 	 * NOTE:
217015516c77SSepherosa Ziehau 	 * The # of RX rings to use is same as the # of channels to use.
217115516c77SSepherosa Ziehau 	 */
217215516c77SSepherosa Ziehau 	ring_cnt = hn_chan_cnt;
217315516c77SSepherosa Ziehau 	if (ring_cnt <= 0) {
217415516c77SSepherosa Ziehau 		/* Default */
217515516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
217615516c77SSepherosa Ziehau 		if (ring_cnt > HN_RING_CNT_DEF_MAX)
217715516c77SSepherosa Ziehau 			ring_cnt = HN_RING_CNT_DEF_MAX;
217815516c77SSepherosa Ziehau 	} else if (ring_cnt > mp_ncpus) {
217915516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
218015516c77SSepherosa Ziehau 	}
218134d68912SSepherosa Ziehau #ifdef RSS
218234d68912SSepherosa Ziehau 	if (ring_cnt > rss_getnumbuckets())
218334d68912SSepherosa Ziehau 		ring_cnt = rss_getnumbuckets();
218434d68912SSepherosa Ziehau #endif
218515516c77SSepherosa Ziehau 
218615516c77SSepherosa Ziehau 	tx_ring_cnt = hn_tx_ring_cnt;
218715516c77SSepherosa Ziehau 	if (tx_ring_cnt <= 0 || tx_ring_cnt > ring_cnt)
218815516c77SSepherosa Ziehau 		tx_ring_cnt = ring_cnt;
218923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
219015516c77SSepherosa Ziehau 	if (hn_use_if_start) {
219115516c77SSepherosa Ziehau 		/* ifnet.if_start only needs one TX ring. */
219215516c77SSepherosa Ziehau 		tx_ring_cnt = 1;
219315516c77SSepherosa Ziehau 	}
219423bf9e15SSepherosa Ziehau #endif
219515516c77SSepherosa Ziehau 
219615516c77SSepherosa Ziehau 	/*
219715516c77SSepherosa Ziehau 	 * Set the leader CPU for channels.
219815516c77SSepherosa Ziehau 	 */
219915516c77SSepherosa Ziehau 	sc->hn_cpu = atomic_fetchadd_int(&hn_cpu_index, ring_cnt) % mp_ncpus;
220015516c77SSepherosa Ziehau 
220115516c77SSepherosa Ziehau 	/*
220215516c77SSepherosa Ziehau 	 * Create enough TX/RX rings, even if only limited number of
220315516c77SSepherosa Ziehau 	 * channels can be allocated.
220415516c77SSepherosa Ziehau 	 */
220515516c77SSepherosa Ziehau 	error = hn_create_tx_data(sc, tx_ring_cnt);
220615516c77SSepherosa Ziehau 	if (error)
220715516c77SSepherosa Ziehau 		goto failed;
220815516c77SSepherosa Ziehau 	error = hn_create_rx_data(sc, ring_cnt);
220915516c77SSepherosa Ziehau 	if (error)
221015516c77SSepherosa Ziehau 		goto failed;
221115516c77SSepherosa Ziehau 
221215516c77SSepherosa Ziehau 	/*
221315516c77SSepherosa Ziehau 	 * Create transaction context for NVS and RNDIS transactions.
221415516c77SSepherosa Ziehau 	 */
221515516c77SSepherosa Ziehau 	sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
221615516c77SSepherosa Ziehau 	    HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
221725641fc7SSepherosa Ziehau 	if (sc->hn_xact == NULL) {
221825641fc7SSepherosa Ziehau 		error = ENXIO;
221915516c77SSepherosa Ziehau 		goto failed;
222025641fc7SSepherosa Ziehau 	}
222125641fc7SSepherosa Ziehau 
222225641fc7SSepherosa Ziehau 	/*
222325641fc7SSepherosa Ziehau 	 * Install orphan handler for the revocation of this device's
222425641fc7SSepherosa Ziehau 	 * primary channel.
222525641fc7SSepherosa Ziehau 	 *
222625641fc7SSepherosa Ziehau 	 * NOTE:
222725641fc7SSepherosa Ziehau 	 * The processing order is critical here:
222825641fc7SSepherosa Ziehau 	 * Install the orphan handler, _before_ testing whether this
222925641fc7SSepherosa Ziehau 	 * device's primary channel has been revoked or not.
223025641fc7SSepherosa Ziehau 	 */
223125641fc7SSepherosa Ziehau 	vmbus_chan_set_orphan(sc->hn_prichan, sc->hn_xact);
223225641fc7SSepherosa Ziehau 	if (vmbus_chan_is_revoked(sc->hn_prichan)) {
223325641fc7SSepherosa Ziehau 		error = ENXIO;
223425641fc7SSepherosa Ziehau 		goto failed;
223525641fc7SSepherosa Ziehau 	}
223615516c77SSepherosa Ziehau 
223715516c77SSepherosa Ziehau 	/*
223815516c77SSepherosa Ziehau 	 * Attach the synthetic parts, i.e. NVS and RNDIS.
223915516c77SSepherosa Ziehau 	 */
224015516c77SSepherosa Ziehau 	error = hn_synth_attach(sc, ETHERMTU);
224115516c77SSepherosa Ziehau 	if (error)
224215516c77SSepherosa Ziehau 		goto failed;
224315516c77SSepherosa Ziehau 
224415516c77SSepherosa Ziehau 	error = hn_rndis_get_eaddr(sc, eaddr);
224515516c77SSepherosa Ziehau 	if (error)
224615516c77SSepherosa Ziehau 		goto failed;
224715516c77SSepherosa Ziehau 
2248eb2fe044SSepherosa Ziehau 	error = hn_rndis_get_mtu(sc, &mtu);
2249eb2fe044SSepherosa Ziehau 	if (error)
2250eb2fe044SSepherosa Ziehau 		mtu = ETHERMTU;
2251eb2fe044SSepherosa Ziehau 	else if (bootverbose)
2252eb2fe044SSepherosa Ziehau 		device_printf(dev, "RNDIS mtu %u\n", mtu);
2253eb2fe044SSepherosa Ziehau 
225415516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
225515516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
225615516c77SSepherosa Ziehau 		/*
225715516c77SSepherosa Ziehau 		 * Reduce TCP segment aggregation limit for multiple
225815516c77SSepherosa Ziehau 		 * RX rings to increase ACK timeliness.
225915516c77SSepherosa Ziehau 		 */
226015516c77SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF);
226115516c77SSepherosa Ziehau 	}
226215516c77SSepherosa Ziehau #endif
226315516c77SSepherosa Ziehau 
226415516c77SSepherosa Ziehau 	/*
2265db76829bSSepherosa Ziehau 	 * Fixup TX/RX stuffs after synthetic parts are attached.
226615516c77SSepherosa Ziehau 	 */
226715516c77SSepherosa Ziehau 	hn_fixup_tx_data(sc);
2268db76829bSSepherosa Ziehau 	hn_fixup_rx_data(sc);
226915516c77SSepherosa Ziehau 
227015516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
227115516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
227215516c77SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "nvs_version", CTLFLAG_RD,
227315516c77SSepherosa Ziehau 	    &sc->hn_nvs_ver, 0, "NVS version");
227415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ndis_version",
227515516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
227615516c77SSepherosa Ziehau 	    hn_ndis_version_sysctl, "A", "NDIS version");
227715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "caps",
227815516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
227915516c77SSepherosa Ziehau 	    hn_caps_sysctl, "A", "capabilities");
228015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist",
228115516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
228215516c77SSepherosa Ziehau 	    hn_hwassist_sysctl, "A", "hwassist");
22839c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_max",
22849c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomax, 0, "max TSO size");
22859c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_maxsegcnt",
22869c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomaxsegcount, 0,
22879c6cae24SSepherosa Ziehau 	    "max # of TSO segments");
22889c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_maxsegsz",
22899c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomaxsegsize, 0,
22909c6cae24SSepherosa Ziehau 	    "max size of TSO segment");
229115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter",
229215516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
229315516c77SSepherosa Ziehau 	    hn_rxfilter_sysctl, "A", "rxfilter");
229415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hash",
229515516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
229615516c77SSepherosa Ziehau 	    hn_rss_hash_sysctl, "A", "RSS hash");
2297642ec226SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hashcap",
2298642ec226SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2299642ec226SSepherosa Ziehau 	    hn_rss_hcap_sysctl, "A", "RSS hash capabilities");
2300642ec226SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "mbuf_hash",
2301642ec226SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2302642ec226SSepherosa Ziehau 	    hn_rss_mbuf_sysctl, "A", "RSS hash for mbufs");
230315516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rss_ind_size",
230415516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rss_ind_size, 0, "RSS indirect entry count");
230534d68912SSepherosa Ziehau #ifndef RSS
230634d68912SSepherosa Ziehau 	/*
230734d68912SSepherosa Ziehau 	 * Don't allow RSS key/indirect table changes, if RSS is defined.
230834d68912SSepherosa Ziehau 	 */
230915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key",
231015516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
231115516c77SSepherosa Ziehau 	    hn_rss_key_sysctl, "IU", "RSS key");
231215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_ind",
231315516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
231415516c77SSepherosa Ziehau 	    hn_rss_ind_sysctl, "IU", "RSS indirect table");
231534d68912SSepherosa Ziehau #endif
2316dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_size",
2317dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_size, 0,
2318dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation size limit");
2319dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_pkts",
2320dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_pkts, 0,
2321dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation count limit");
2322dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_align",
2323dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_align, 0,
2324dc13fee6SSepherosa Ziehau 	    "RNDIS packet transmission aggregation alignment");
2325dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_size",
2326dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
2327dc13fee6SSepherosa Ziehau 	    hn_txagg_size_sysctl, "I",
2328dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation size, 0 -- disable, -1 -- auto");
2329dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pkts",
2330dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
2331dc13fee6SSepherosa Ziehau 	    hn_txagg_pkts_sysctl, "I",
2332dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation packets, "
2333dc13fee6SSepherosa Ziehau 	    "0 -- disable, -1 -- auto");
23346c1204dfSSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "polling",
23356c1204dfSSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
23366c1204dfSSepherosa Ziehau 	    hn_polling_sysctl, "I",
23376c1204dfSSepherosa Ziehau 	    "Polling frequency: [100,1000000], 0 disable polling");
233840d60d6eSDexuan Cui 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf",
233940d60d6eSDexuan Cui 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
234040d60d6eSDexuan Cui 	    hn_vf_sysctl, "A", "Virtual Function's name");
23419c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
2342499c3e17SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxvf",
2343499c3e17SSepherosa Ziehau 		    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2344499c3e17SSepherosa Ziehau 		    hn_rxvf_sysctl, "A", "activated Virtual Function's name");
23459c6cae24SSepherosa Ziehau 	} else {
23469c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_enabled",
23479c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
23489c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_enabled_sysctl, "I",
23499c6cae24SSepherosa Ziehau 		    "Transparent VF enabled");
23509c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_accbpf",
23519c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
23529c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_accbpf_sysctl, "I",
23539c6cae24SSepherosa Ziehau 		    "Accurate BPF for transparent VF");
23549c6cae24SSepherosa Ziehau 	}
235515516c77SSepherosa Ziehau 
235615516c77SSepherosa Ziehau 	/*
235715516c77SSepherosa Ziehau 	 * Setup the ifmedia, which has been initialized earlier.
235815516c77SSepherosa Ziehau 	 */
235915516c77SSepherosa Ziehau 	ifmedia_add(&sc->hn_media, IFM_ETHER | IFM_AUTO, 0, NULL);
236015516c77SSepherosa Ziehau 	ifmedia_set(&sc->hn_media, IFM_ETHER | IFM_AUTO);
236115516c77SSepherosa Ziehau 	/* XXX ifmedia_set really should do this for us */
236215516c77SSepherosa Ziehau 	sc->hn_media.ifm_media = sc->hn_media.ifm_cur->ifm_media;
236315516c77SSepherosa Ziehau 
236415516c77SSepherosa Ziehau 	/*
236515516c77SSepherosa Ziehau 	 * Setup the ifnet for this interface.
236615516c77SSepherosa Ziehau 	 */
236715516c77SSepherosa Ziehau 
236815516c77SSepherosa Ziehau 	ifp->if_baudrate = IF_Gbps(10);
2369e87c4940SGleb Smirnoff 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
237015516c77SSepherosa Ziehau 	ifp->if_ioctl = hn_ioctl;
237115516c77SSepherosa Ziehau 	ifp->if_init = hn_init;
237223bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
237315516c77SSepherosa Ziehau 	if (hn_use_if_start) {
237415516c77SSepherosa Ziehau 		int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]);
237515516c77SSepherosa Ziehau 
237615516c77SSepherosa Ziehau 		ifp->if_start = hn_start;
237715516c77SSepherosa Ziehau 		IFQ_SET_MAXLEN(&ifp->if_snd, qdepth);
237815516c77SSepherosa Ziehau 		ifp->if_snd.ifq_drv_maxlen = qdepth - 1;
237915516c77SSepherosa Ziehau 		IFQ_SET_READY(&ifp->if_snd);
238023bf9e15SSepherosa Ziehau 	} else
238123bf9e15SSepherosa Ziehau #endif
238223bf9e15SSepherosa Ziehau 	{
238315516c77SSepherosa Ziehau 		ifp->if_transmit = hn_transmit;
238415516c77SSepherosa Ziehau 		ifp->if_qflush = hn_xmit_qflush;
238515516c77SSepherosa Ziehau 	}
238615516c77SSepherosa Ziehau 
23879c6cae24SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_LRO | IFCAP_LINKSTATE;
238815516c77SSepherosa Ziehau #ifdef foo
238915516c77SSepherosa Ziehau 	/* We can't diff IPv6 packets from IPv4 packets on RX path. */
239015516c77SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_RXCSUM_IPV6;
239115516c77SSepherosa Ziehau #endif
239215516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_VLAN) {
239315516c77SSepherosa Ziehau 		/* XXX not sure about VLAN_MTU. */
239415516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
239515516c77SSepherosa Ziehau 	}
239615516c77SSepherosa Ziehau 
239715516c77SSepherosa Ziehau 	ifp->if_hwassist = sc->hn_tx_ring[0].hn_csum_assist;
239815516c77SSepherosa Ziehau 	if (ifp->if_hwassist & HN_CSUM_IP_MASK)
239915516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TXCSUM;
240015516c77SSepherosa Ziehau 	if (ifp->if_hwassist & HN_CSUM_IP6_MASK)
240115516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TXCSUM_IPV6;
240215516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO4) {
240315516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TSO4;
240415516c77SSepherosa Ziehau 		ifp->if_hwassist |= CSUM_IP_TSO;
240515516c77SSepherosa Ziehau 	}
240615516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO6) {
240715516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TSO6;
240815516c77SSepherosa Ziehau 		ifp->if_hwassist |= CSUM_IP6_TSO;
240915516c77SSepherosa Ziehau 	}
241015516c77SSepherosa Ziehau 
241115516c77SSepherosa Ziehau 	/* Enable all available capabilities by default. */
241215516c77SSepherosa Ziehau 	ifp->if_capenable = ifp->if_capabilities;
241315516c77SSepherosa Ziehau 
24147960e6baSSepherosa Ziehau 	/*
24157960e6baSSepherosa Ziehau 	 * Disable IPv6 TSO and TXCSUM by default, they still can
24167960e6baSSepherosa Ziehau 	 * be enabled through SIOCSIFCAP.
24177960e6baSSepherosa Ziehau 	 */
24187960e6baSSepherosa Ziehau 	ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6 | IFCAP_TSO6);
24197960e6baSSepherosa Ziehau 	ifp->if_hwassist &= ~(HN_CSUM_IP6_MASK | CSUM_IP6_TSO);
24207960e6baSSepherosa Ziehau 
242115516c77SSepherosa Ziehau 	if (ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) {
24229c6cae24SSepherosa Ziehau 		/*
24239c6cae24SSepherosa Ziehau 		 * Lock hn_set_tso_maxsize() to simplify its
24249c6cae24SSepherosa Ziehau 		 * internal logic.
24259c6cae24SSepherosa Ziehau 		 */
24269c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
242715516c77SSepherosa Ziehau 		hn_set_tso_maxsize(sc, hn_tso_maxlen, ETHERMTU);
24289c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
242915516c77SSepherosa Ziehau 		ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX;
243015516c77SSepherosa Ziehau 		ifp->if_hw_tsomaxsegsize = PAGE_SIZE;
243115516c77SSepherosa Ziehau 	}
243215516c77SSepherosa Ziehau 
243315516c77SSepherosa Ziehau 	ether_ifattach(ifp, eaddr);
243415516c77SSepherosa Ziehau 
243515516c77SSepherosa Ziehau 	if ((ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) && bootverbose) {
243615516c77SSepherosa Ziehau 		if_printf(ifp, "TSO segcnt %u segsz %u\n",
243715516c77SSepherosa Ziehau 		    ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize);
243815516c77SSepherosa Ziehau 	}
2439eb2fe044SSepherosa Ziehau 	if (mtu < ETHERMTU) {
2440eb2fe044SSepherosa Ziehau 		if_printf(ifp, "fixup mtu %u -> %u\n", ifp->if_mtu, mtu);
2441eb2fe044SSepherosa Ziehau 		ifp->if_mtu = mtu;
2442eb2fe044SSepherosa Ziehau 	}
244315516c77SSepherosa Ziehau 
244415516c77SSepherosa Ziehau 	/* Inform the upper layer about the long frame support. */
244515516c77SSepherosa Ziehau 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
244615516c77SSepherosa Ziehau 
244715516c77SSepherosa Ziehau 	/*
244815516c77SSepherosa Ziehau 	 * Kick off link status check.
244915516c77SSepherosa Ziehau 	 */
245015516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
245115516c77SSepherosa Ziehau 	hn_update_link_status(sc);
245215516c77SSepherosa Ziehau 
24539c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
24545bdfd3fdSDexuan Cui 		sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event,
24555bdfd3fdSDexuan Cui 		    hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY);
24565bdfd3fdSDexuan Cui 		sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event,
24575bdfd3fdSDexuan Cui 		    hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY);
24589c6cae24SSepherosa Ziehau 	} else {
24599c6cae24SSepherosa Ziehau 		sc->hn_ifnet_lnkhand = EVENTHANDLER_REGISTER(ifnet_link_event,
24609c6cae24SSepherosa Ziehau 		    hn_ifnet_lnkevent, sc, EVENTHANDLER_PRI_ANY);
24619c6cae24SSepherosa Ziehau 	}
24625bdfd3fdSDexuan Cui 
2463f41e0df4SSepherosa Ziehau 	/*
2464f41e0df4SSepherosa Ziehau 	 * NOTE:
2465f41e0df4SSepherosa Ziehau 	 * Subscribe ether_ifattach event, instead of ifnet_arrival event,
2466f41e0df4SSepherosa Ziehau 	 * since interface's LLADDR is needed; interface LLADDR is not
2467f41e0df4SSepherosa Ziehau 	 * available when ifnet_arrival event is triggered.
2468f41e0df4SSepherosa Ziehau 	 */
2469499c3e17SSepherosa Ziehau 	sc->hn_ifnet_atthand = EVENTHANDLER_REGISTER(ether_ifattach_event,
2470499c3e17SSepherosa Ziehau 	    hn_ifnet_attevent, sc, EVENTHANDLER_PRI_ANY);
2471499c3e17SSepherosa Ziehau 	sc->hn_ifnet_dethand = EVENTHANDLER_REGISTER(ifnet_departure_event,
2472499c3e17SSepherosa Ziehau 	    hn_ifnet_detevent, sc, EVENTHANDLER_PRI_ANY);
2473499c3e17SSepherosa Ziehau 
247415516c77SSepherosa Ziehau 	return (0);
247515516c77SSepherosa Ziehau failed:
247615516c77SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
247715516c77SSepherosa Ziehau 		hn_synth_detach(sc);
247815516c77SSepherosa Ziehau 	hn_detach(dev);
247915516c77SSepherosa Ziehau 	return (error);
248015516c77SSepherosa Ziehau }
248115516c77SSepherosa Ziehau 
248215516c77SSepherosa Ziehau static int
248315516c77SSepherosa Ziehau hn_detach(device_t dev)
248415516c77SSepherosa Ziehau {
248515516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
2486499c3e17SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp, *vf_ifp;
248715516c77SSepherosa Ziehau 
24889c6cae24SSepherosa Ziehau 	if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
24899c6cae24SSepherosa Ziehau 		/*
24909c6cae24SSepherosa Ziehau 		 * In case that the vmbus missed the orphan handler
24919c6cae24SSepherosa Ziehau 		 * installation.
24929c6cae24SSepherosa Ziehau 		 */
24939c6cae24SSepherosa Ziehau 		vmbus_xact_ctx_orphan(sc->hn_xact);
24949c6cae24SSepherosa Ziehau 	}
24959c6cae24SSepherosa Ziehau 
24965bdfd3fdSDexuan Cui 	if (sc->hn_ifaddr_evthand != NULL)
24975bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand);
24985bdfd3fdSDexuan Cui 	if (sc->hn_ifnet_evthand != NULL)
24995bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand);
2500499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_atthand != NULL) {
2501499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ether_ifattach_event,
2502499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_atthand);
2503499c3e17SSepherosa Ziehau 	}
2504499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_dethand != NULL) {
2505499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
2506499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_dethand);
2507499c3e17SSepherosa Ziehau 	}
25089c6cae24SSepherosa Ziehau 	if (sc->hn_ifnet_lnkhand != NULL)
25099c6cae24SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_link_event, sc->hn_ifnet_lnkhand);
2510499c3e17SSepherosa Ziehau 
2511499c3e17SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
2512499c3e17SSepherosa Ziehau 	__compiler_membar();
2513499c3e17SSepherosa Ziehau 	if (vf_ifp != NULL)
2514499c3e17SSepherosa Ziehau 		hn_ifnet_detevent(sc, vf_ifp);
25155bdfd3fdSDexuan Cui 
251615516c77SSepherosa Ziehau 	if (device_is_attached(dev)) {
251715516c77SSepherosa Ziehau 		HN_LOCK(sc);
251815516c77SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
251915516c77SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
25205bdfd3fdSDexuan Cui 				hn_stop(sc, true);
252115516c77SSepherosa Ziehau 			/*
252215516c77SSepherosa Ziehau 			 * NOTE:
252315516c77SSepherosa Ziehau 			 * hn_stop() only suspends data, so managment
252415516c77SSepherosa Ziehau 			 * stuffs have to be suspended manually here.
252515516c77SSepherosa Ziehau 			 */
252615516c77SSepherosa Ziehau 			hn_suspend_mgmt(sc);
252715516c77SSepherosa Ziehau 			hn_synth_detach(sc);
252815516c77SSepherosa Ziehau 		}
252915516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
253015516c77SSepherosa Ziehau 		ether_ifdetach(ifp);
253115516c77SSepherosa Ziehau 	}
253215516c77SSepherosa Ziehau 
253315516c77SSepherosa Ziehau 	ifmedia_removeall(&sc->hn_media);
253415516c77SSepherosa Ziehau 	hn_destroy_rx_data(sc);
253515516c77SSepherosa Ziehau 	hn_destroy_tx_data(sc);
253615516c77SSepherosa Ziehau 
25370e11868dSSepherosa Ziehau 	if (sc->hn_tx_taskqs != NULL && sc->hn_tx_taskqs != hn_tx_taskque) {
2538fdd0222aSSepherosa Ziehau 		int i;
2539fdd0222aSSepherosa Ziehau 
2540fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
2541fdd0222aSSepherosa Ziehau 			taskqueue_free(sc->hn_tx_taskqs[i]);
2542fdd0222aSSepherosa Ziehau 		free(sc->hn_tx_taskqs, M_DEVBUF);
2543fdd0222aSSepherosa Ziehau 	}
254415516c77SSepherosa Ziehau 	taskqueue_free(sc->hn_mgmt_taskq0);
25459c6cae24SSepherosa Ziehau 	if (sc->hn_vf_taskq != NULL)
25469c6cae24SSepherosa Ziehau 		taskqueue_free(sc->hn_vf_taskq);
254715516c77SSepherosa Ziehau 
254825641fc7SSepherosa Ziehau 	if (sc->hn_xact != NULL) {
254925641fc7SSepherosa Ziehau 		/*
255025641fc7SSepherosa Ziehau 		 * Uninstall the orphan handler _before_ the xact is
255125641fc7SSepherosa Ziehau 		 * destructed.
255225641fc7SSepherosa Ziehau 		 */
255325641fc7SSepherosa Ziehau 		vmbus_chan_unset_orphan(sc->hn_prichan);
255415516c77SSepherosa Ziehau 		vmbus_xact_ctx_destroy(sc->hn_xact);
255525641fc7SSepherosa Ziehau 	}
255615516c77SSepherosa Ziehau 
255715516c77SSepherosa Ziehau 	if_free(ifp);
255815516c77SSepherosa Ziehau 
255915516c77SSepherosa Ziehau 	HN_LOCK_DESTROY(sc);
25609c6cae24SSepherosa Ziehau 	rm_destroy(&sc->hn_vf_lock);
256115516c77SSepherosa Ziehau 	return (0);
256215516c77SSepherosa Ziehau }
256315516c77SSepherosa Ziehau 
256415516c77SSepherosa Ziehau static int
256515516c77SSepherosa Ziehau hn_shutdown(device_t dev)
256615516c77SSepherosa Ziehau {
256715516c77SSepherosa Ziehau 
256815516c77SSepherosa Ziehau 	return (0);
256915516c77SSepherosa Ziehau }
257015516c77SSepherosa Ziehau 
257115516c77SSepherosa Ziehau static void
257215516c77SSepherosa Ziehau hn_link_status(struct hn_softc *sc)
257315516c77SSepherosa Ziehau {
257415516c77SSepherosa Ziehau 	uint32_t link_status;
257515516c77SSepherosa Ziehau 	int error;
257615516c77SSepherosa Ziehau 
257715516c77SSepherosa Ziehau 	error = hn_rndis_get_linkstatus(sc, &link_status);
257815516c77SSepherosa Ziehau 	if (error) {
257915516c77SSepherosa Ziehau 		/* XXX what to do? */
258015516c77SSepherosa Ziehau 		return;
258115516c77SSepherosa Ziehau 	}
258215516c77SSepherosa Ziehau 
258315516c77SSepherosa Ziehau 	if (link_status == NDIS_MEDIA_STATE_CONNECTED)
258415516c77SSepherosa Ziehau 		sc->hn_link_flags |= HN_LINK_FLAG_LINKUP;
258515516c77SSepherosa Ziehau 	else
258615516c77SSepherosa Ziehau 		sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
258715516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp,
258815516c77SSepherosa Ziehau 	    (sc->hn_link_flags & HN_LINK_FLAG_LINKUP) ?
258915516c77SSepherosa Ziehau 	    LINK_STATE_UP : LINK_STATE_DOWN);
259015516c77SSepherosa Ziehau }
259115516c77SSepherosa Ziehau 
259215516c77SSepherosa Ziehau static void
259315516c77SSepherosa Ziehau hn_link_taskfunc(void *xsc, int pending __unused)
259415516c77SSepherosa Ziehau {
259515516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
259615516c77SSepherosa Ziehau 
259715516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
259815516c77SSepherosa Ziehau 		return;
259915516c77SSepherosa Ziehau 	hn_link_status(sc);
260015516c77SSepherosa Ziehau }
260115516c77SSepherosa Ziehau 
260215516c77SSepherosa Ziehau static void
260315516c77SSepherosa Ziehau hn_netchg_init_taskfunc(void *xsc, int pending __unused)
260415516c77SSepherosa Ziehau {
260515516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
260615516c77SSepherosa Ziehau 
260715516c77SSepherosa Ziehau 	/* Prevent any link status checks from running. */
260815516c77SSepherosa Ziehau 	sc->hn_link_flags |= HN_LINK_FLAG_NETCHG;
260915516c77SSepherosa Ziehau 
261015516c77SSepherosa Ziehau 	/*
261115516c77SSepherosa Ziehau 	 * Fake up a [link down --> link up] state change; 5 seconds
261215516c77SSepherosa Ziehau 	 * delay is used, which closely simulates miibus reaction
261315516c77SSepherosa Ziehau 	 * upon link down event.
261415516c77SSepherosa Ziehau 	 */
261515516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
261615516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
261715516c77SSepherosa Ziehau 	taskqueue_enqueue_timeout(sc->hn_mgmt_taskq0,
261815516c77SSepherosa Ziehau 	    &sc->hn_netchg_status, 5 * hz);
261915516c77SSepherosa Ziehau }
262015516c77SSepherosa Ziehau 
262115516c77SSepherosa Ziehau static void
262215516c77SSepherosa Ziehau hn_netchg_status_taskfunc(void *xsc, int pending __unused)
262315516c77SSepherosa Ziehau {
262415516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
262515516c77SSepherosa Ziehau 
262615516c77SSepherosa Ziehau 	/* Re-allow link status checks. */
262715516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_NETCHG;
262815516c77SSepherosa Ziehau 	hn_link_status(sc);
262915516c77SSepherosa Ziehau }
263015516c77SSepherosa Ziehau 
263115516c77SSepherosa Ziehau static void
263215516c77SSepherosa Ziehau hn_update_link_status(struct hn_softc *sc)
263315516c77SSepherosa Ziehau {
263415516c77SSepherosa Ziehau 
263515516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
263615516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
263715516c77SSepherosa Ziehau }
263815516c77SSepherosa Ziehau 
263915516c77SSepherosa Ziehau static void
264015516c77SSepherosa Ziehau hn_change_network(struct hn_softc *sc)
264115516c77SSepherosa Ziehau {
264215516c77SSepherosa Ziehau 
264315516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
264415516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_netchg_init);
264515516c77SSepherosa Ziehau }
264615516c77SSepherosa Ziehau 
264715516c77SSepherosa Ziehau static __inline int
264815516c77SSepherosa Ziehau hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
264915516c77SSepherosa Ziehau     struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
265015516c77SSepherosa Ziehau {
265115516c77SSepherosa Ziehau 	struct mbuf *m = *m_head;
265215516c77SSepherosa Ziehau 	int error;
265315516c77SSepherosa Ziehau 
265415516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID, ("txd uses chim"));
265515516c77SSepherosa Ziehau 
265615516c77SSepherosa Ziehau 	error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag, txd->data_dmap,
265715516c77SSepherosa Ziehau 	    m, segs, nsegs, BUS_DMA_NOWAIT);
265815516c77SSepherosa Ziehau 	if (error == EFBIG) {
265915516c77SSepherosa Ziehau 		struct mbuf *m_new;
266015516c77SSepherosa Ziehau 
266115516c77SSepherosa Ziehau 		m_new = m_collapse(m, M_NOWAIT, HN_TX_DATA_SEGCNT_MAX);
266215516c77SSepherosa Ziehau 		if (m_new == NULL)
266315516c77SSepherosa Ziehau 			return ENOBUFS;
266415516c77SSepherosa Ziehau 		else
266515516c77SSepherosa Ziehau 			*m_head = m = m_new;
266615516c77SSepherosa Ziehau 		txr->hn_tx_collapsed++;
266715516c77SSepherosa Ziehau 
266815516c77SSepherosa Ziehau 		error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag,
266915516c77SSepherosa Ziehau 		    txd->data_dmap, m, segs, nsegs, BUS_DMA_NOWAIT);
267015516c77SSepherosa Ziehau 	}
267115516c77SSepherosa Ziehau 	if (!error) {
267215516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag, txd->data_dmap,
267315516c77SSepherosa Ziehau 		    BUS_DMASYNC_PREWRITE);
267415516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_DMAMAP;
267515516c77SSepherosa Ziehau 	}
267615516c77SSepherosa Ziehau 	return error;
267715516c77SSepherosa Ziehau }
267815516c77SSepherosa Ziehau 
267915516c77SSepherosa Ziehau static __inline int
268015516c77SSepherosa Ziehau hn_txdesc_put(struct hn_tx_ring *txr, struct hn_txdesc *txd)
268115516c77SSepherosa Ziehau {
268215516c77SSepherosa Ziehau 
268315516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONLIST) == 0,
268415516c77SSepherosa Ziehau 	    ("put an onlist txd %#x", txd->flags));
2685dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2686dc13fee6SSepherosa Ziehau 	    ("put an onagg txd %#x", txd->flags));
268715516c77SSepherosa Ziehau 
268815516c77SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
268915516c77SSepherosa Ziehau 	if (atomic_fetchadd_int(&txd->refs, -1) != 1)
269015516c77SSepherosa Ziehau 		return 0;
269115516c77SSepherosa Ziehau 
2692dc13fee6SSepherosa Ziehau 	if (!STAILQ_EMPTY(&txd->agg_list)) {
2693dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tmp_txd;
2694dc13fee6SSepherosa Ziehau 
2695dc13fee6SSepherosa Ziehau 		while ((tmp_txd = STAILQ_FIRST(&txd->agg_list)) != NULL) {
2696dc13fee6SSepherosa Ziehau 			int freed;
2697dc13fee6SSepherosa Ziehau 
2698dc13fee6SSepherosa Ziehau 			KASSERT(STAILQ_EMPTY(&tmp_txd->agg_list),
2699dc13fee6SSepherosa Ziehau 			    ("resursive aggregation on aggregated txdesc"));
2700dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_ONAGG),
2701dc13fee6SSepherosa Ziehau 			    ("not aggregated txdesc"));
2702dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
2703dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc uses dmamap"));
2704dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
2705dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc consumes "
2706dc13fee6SSepherosa Ziehau 			     "chimney sending buffer"));
2707dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_size == 0,
2708dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc has non-zero "
2709dc13fee6SSepherosa Ziehau 			     "chimney sending size"));
2710dc13fee6SSepherosa Ziehau 
2711dc13fee6SSepherosa Ziehau 			STAILQ_REMOVE_HEAD(&txd->agg_list, agg_link);
2712dc13fee6SSepherosa Ziehau 			tmp_txd->flags &= ~HN_TXD_FLAG_ONAGG;
2713dc13fee6SSepherosa Ziehau 			freed = hn_txdesc_put(txr, tmp_txd);
2714dc13fee6SSepherosa Ziehau 			KASSERT(freed, ("failed to free aggregated txdesc"));
2715dc13fee6SSepherosa Ziehau 		}
2716dc13fee6SSepherosa Ziehau 	}
2717dc13fee6SSepherosa Ziehau 
271815516c77SSepherosa Ziehau 	if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) {
271915516c77SSepherosa Ziehau 		KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
272015516c77SSepherosa Ziehau 		    ("chim txd uses dmamap"));
272115516c77SSepherosa Ziehau 		hn_chim_free(txr->hn_sc, txd->chim_index);
272215516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
2723dc13fee6SSepherosa Ziehau 		txd->chim_size = 0;
272415516c77SSepherosa Ziehau 	} else if (txd->flags & HN_TXD_FLAG_DMAMAP) {
272515516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag,
272615516c77SSepherosa Ziehau 		    txd->data_dmap, BUS_DMASYNC_POSTWRITE);
272715516c77SSepherosa Ziehau 		bus_dmamap_unload(txr->hn_tx_data_dtag,
272815516c77SSepherosa Ziehau 		    txd->data_dmap);
272915516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_DMAMAP;
273015516c77SSepherosa Ziehau 	}
273115516c77SSepherosa Ziehau 
273215516c77SSepherosa Ziehau 	if (txd->m != NULL) {
273315516c77SSepherosa Ziehau 		m_freem(txd->m);
273415516c77SSepherosa Ziehau 		txd->m = NULL;
273515516c77SSepherosa Ziehau 	}
273615516c77SSepherosa Ziehau 
273715516c77SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONLIST;
273815516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
273915516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
274015516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_avail >= 0 &&
274115516c77SSepherosa Ziehau 	    txr->hn_txdesc_avail < txr->hn_txdesc_cnt,
274215516c77SSepherosa Ziehau 	    ("txdesc_put: invalid txd avail %d", txr->hn_txdesc_avail));
274315516c77SSepherosa Ziehau 	txr->hn_txdesc_avail++;
274415516c77SSepherosa Ziehau 	SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
274515516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
274685e4ae1eSSepherosa Ziehau #else	/* HN_USE_TXDESC_BUFRING */
274785e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
274815516c77SSepherosa Ziehau 	atomic_add_int(&txr->hn_txdesc_avail, 1);
274915516c77SSepherosa Ziehau #endif
275085e4ae1eSSepherosa Ziehau 	buf_ring_enqueue(txr->hn_txdesc_br, txd);
275185e4ae1eSSepherosa Ziehau #endif	/* !HN_USE_TXDESC_BUFRING */
275215516c77SSepherosa Ziehau 
275315516c77SSepherosa Ziehau 	return 1;
275415516c77SSepherosa Ziehau }
275515516c77SSepherosa Ziehau 
275615516c77SSepherosa Ziehau static __inline struct hn_txdesc *
275715516c77SSepherosa Ziehau hn_txdesc_get(struct hn_tx_ring *txr)
275815516c77SSepherosa Ziehau {
275915516c77SSepherosa Ziehau 	struct hn_txdesc *txd;
276015516c77SSepherosa Ziehau 
276115516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
276215516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
276315516c77SSepherosa Ziehau 	txd = SLIST_FIRST(&txr->hn_txlist);
276415516c77SSepherosa Ziehau 	if (txd != NULL) {
276515516c77SSepherosa Ziehau 		KASSERT(txr->hn_txdesc_avail > 0,
276615516c77SSepherosa Ziehau 		    ("txdesc_get: invalid txd avail %d", txr->hn_txdesc_avail));
276715516c77SSepherosa Ziehau 		txr->hn_txdesc_avail--;
276815516c77SSepherosa Ziehau 		SLIST_REMOVE_HEAD(&txr->hn_txlist, link);
276915516c77SSepherosa Ziehau 	}
277015516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
277115516c77SSepherosa Ziehau #else
277215516c77SSepherosa Ziehau 	txd = buf_ring_dequeue_sc(txr->hn_txdesc_br);
277315516c77SSepherosa Ziehau #endif
277415516c77SSepherosa Ziehau 
277515516c77SSepherosa Ziehau 	if (txd != NULL) {
277615516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
277785e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
277815516c77SSepherosa Ziehau 		atomic_subtract_int(&txr->hn_txdesc_avail, 1);
277915516c77SSepherosa Ziehau #endif
278085e4ae1eSSepherosa Ziehau #endif	/* HN_USE_TXDESC_BUFRING */
278115516c77SSepherosa Ziehau 		KASSERT(txd->m == NULL && txd->refs == 0 &&
2782dc13fee6SSepherosa Ziehau 		    STAILQ_EMPTY(&txd->agg_list) &&
278315516c77SSepherosa Ziehau 		    txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
2784dc13fee6SSepherosa Ziehau 		    txd->chim_size == 0 &&
278515516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONLIST) &&
2786dc13fee6SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONAGG) == 0 &&
278715516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("invalid txd"));
278815516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_ONLIST;
278915516c77SSepherosa Ziehau 		txd->refs = 1;
279015516c77SSepherosa Ziehau 	}
279115516c77SSepherosa Ziehau 	return txd;
279215516c77SSepherosa Ziehau }
279315516c77SSepherosa Ziehau 
279415516c77SSepherosa Ziehau static __inline void
279515516c77SSepherosa Ziehau hn_txdesc_hold(struct hn_txdesc *txd)
279615516c77SSepherosa Ziehau {
279715516c77SSepherosa Ziehau 
279815516c77SSepherosa Ziehau 	/* 0->1 transition will never work */
279925641fc7SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
280015516c77SSepherosa Ziehau 	atomic_add_int(&txd->refs, 1);
280115516c77SSepherosa Ziehau }
280215516c77SSepherosa Ziehau 
2803dc13fee6SSepherosa Ziehau static __inline void
2804dc13fee6SSepherosa Ziehau hn_txdesc_agg(struct hn_txdesc *agg_txd, struct hn_txdesc *txd)
2805dc13fee6SSepherosa Ziehau {
2806dc13fee6SSepherosa Ziehau 
2807dc13fee6SSepherosa Ziehau 	KASSERT((agg_txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2808dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on aggregating txdesc"));
2809dc13fee6SSepherosa Ziehau 
2810dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2811dc13fee6SSepherosa Ziehau 	    ("already aggregated"));
2812dc13fee6SSepherosa Ziehau 	KASSERT(STAILQ_EMPTY(&txd->agg_list),
2813dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on to-be-aggregated txdesc"));
2814dc13fee6SSepherosa Ziehau 
2815dc13fee6SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONAGG;
2816dc13fee6SSepherosa Ziehau 	STAILQ_INSERT_TAIL(&agg_txd->agg_list, txd, agg_link);
2817dc13fee6SSepherosa Ziehau }
2818dc13fee6SSepherosa Ziehau 
281915516c77SSepherosa Ziehau static bool
282015516c77SSepherosa Ziehau hn_tx_ring_pending(struct hn_tx_ring *txr)
282115516c77SSepherosa Ziehau {
282215516c77SSepherosa Ziehau 	bool pending = false;
282315516c77SSepherosa Ziehau 
282415516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
282515516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
282615516c77SSepherosa Ziehau 	if (txr->hn_txdesc_avail != txr->hn_txdesc_cnt)
282715516c77SSepherosa Ziehau 		pending = true;
282815516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
282915516c77SSepherosa Ziehau #else
283015516c77SSepherosa Ziehau 	if (!buf_ring_full(txr->hn_txdesc_br))
283115516c77SSepherosa Ziehau 		pending = true;
283215516c77SSepherosa Ziehau #endif
283315516c77SSepherosa Ziehau 	return (pending);
283415516c77SSepherosa Ziehau }
283515516c77SSepherosa Ziehau 
283615516c77SSepherosa Ziehau static __inline void
283715516c77SSepherosa Ziehau hn_txeof(struct hn_tx_ring *txr)
283815516c77SSepherosa Ziehau {
283915516c77SSepherosa Ziehau 	txr->hn_has_txeof = 0;
284015516c77SSepherosa Ziehau 	txr->hn_txeof(txr);
284115516c77SSepherosa Ziehau }
284215516c77SSepherosa Ziehau 
284315516c77SSepherosa Ziehau static void
284415516c77SSepherosa Ziehau hn_txpkt_done(struct hn_nvs_sendctx *sndc, struct hn_softc *sc,
284515516c77SSepherosa Ziehau     struct vmbus_channel *chan, const void *data __unused, int dlen __unused)
284615516c77SSepherosa Ziehau {
284715516c77SSepherosa Ziehau 	struct hn_txdesc *txd = sndc->hn_cbarg;
284815516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
284915516c77SSepherosa Ziehau 
285015516c77SSepherosa Ziehau 	txr = txd->txr;
285115516c77SSepherosa Ziehau 	KASSERT(txr->hn_chan == chan,
285215516c77SSepherosa Ziehau 	    ("channel mismatch, on chan%u, should be chan%u",
2853aa1a2adcSSepherosa Ziehau 	     vmbus_chan_id(chan), vmbus_chan_id(txr->hn_chan)));
285415516c77SSepherosa Ziehau 
285515516c77SSepherosa Ziehau 	txr->hn_has_txeof = 1;
285615516c77SSepherosa Ziehau 	hn_txdesc_put(txr, txd);
285715516c77SSepherosa Ziehau 
285815516c77SSepherosa Ziehau 	++txr->hn_txdone_cnt;
285915516c77SSepherosa Ziehau 	if (txr->hn_txdone_cnt >= HN_EARLY_TXEOF_THRESH) {
286015516c77SSepherosa Ziehau 		txr->hn_txdone_cnt = 0;
286115516c77SSepherosa Ziehau 		if (txr->hn_oactive)
286215516c77SSepherosa Ziehau 			hn_txeof(txr);
286315516c77SSepherosa Ziehau 	}
286415516c77SSepherosa Ziehau }
286515516c77SSepherosa Ziehau 
286615516c77SSepherosa Ziehau static void
286715516c77SSepherosa Ziehau hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
286815516c77SSepherosa Ziehau {
286915516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
287015516c77SSepherosa Ziehau 	tcp_lro_flush_all(&rxr->hn_lro);
287115516c77SSepherosa Ziehau #endif
287215516c77SSepherosa Ziehau 
287315516c77SSepherosa Ziehau 	/*
287415516c77SSepherosa Ziehau 	 * NOTE:
287515516c77SSepherosa Ziehau 	 * 'txr' could be NULL, if multiple channels and
287615516c77SSepherosa Ziehau 	 * ifnet.if_start method are enabled.
287715516c77SSepherosa Ziehau 	 */
287815516c77SSepherosa Ziehau 	if (txr == NULL || !txr->hn_has_txeof)
287915516c77SSepherosa Ziehau 		return;
288015516c77SSepherosa Ziehau 
288115516c77SSepherosa Ziehau 	txr->hn_txdone_cnt = 0;
288215516c77SSepherosa Ziehau 	hn_txeof(txr);
288315516c77SSepherosa Ziehau }
288415516c77SSepherosa Ziehau 
288515516c77SSepherosa Ziehau static __inline uint32_t
288615516c77SSepherosa Ziehau hn_rndis_pktmsg_offset(uint32_t ofs)
288715516c77SSepherosa Ziehau {
288815516c77SSepherosa Ziehau 
288915516c77SSepherosa Ziehau 	KASSERT(ofs >= sizeof(struct rndis_packet_msg),
289015516c77SSepherosa Ziehau 	    ("invalid RNDIS packet msg offset %u", ofs));
289115516c77SSepherosa Ziehau 	return (ofs - __offsetof(struct rndis_packet_msg, rm_dataoffset));
289215516c77SSepherosa Ziehau }
289315516c77SSepherosa Ziehau 
289415516c77SSepherosa Ziehau static __inline void *
289515516c77SSepherosa Ziehau hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
289615516c77SSepherosa Ziehau     size_t pi_dlen, uint32_t pi_type)
289715516c77SSepherosa Ziehau {
289815516c77SSepherosa Ziehau 	const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
289915516c77SSepherosa Ziehau 	struct rndis_pktinfo *pi;
290015516c77SSepherosa Ziehau 
290115516c77SSepherosa Ziehau 	KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
290215516c77SSepherosa Ziehau 	    ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
290315516c77SSepherosa Ziehau 
290415516c77SSepherosa Ziehau 	/*
290515516c77SSepherosa Ziehau 	 * Per-packet-info does not move; it only grows.
290615516c77SSepherosa Ziehau 	 *
290715516c77SSepherosa Ziehau 	 * NOTE:
290815516c77SSepherosa Ziehau 	 * rm_pktinfooffset in this phase counts from the beginning
290915516c77SSepherosa Ziehau 	 * of rndis_packet_msg.
291015516c77SSepherosa Ziehau 	 */
291115516c77SSepherosa Ziehau 	KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
291215516c77SSepherosa Ziehau 	    ("%u pktinfo overflows RNDIS packet msg", pi_type));
291315516c77SSepherosa Ziehau 	pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
291415516c77SSepherosa Ziehau 	    pkt->rm_pktinfolen);
291515516c77SSepherosa Ziehau 	pkt->rm_pktinfolen += pi_size;
291615516c77SSepherosa Ziehau 
291715516c77SSepherosa Ziehau 	pi->rm_size = pi_size;
291815516c77SSepherosa Ziehau 	pi->rm_type = pi_type;
291915516c77SSepherosa Ziehau 	pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
292015516c77SSepherosa Ziehau 
292115516c77SSepherosa Ziehau 	return (pi->rm_data);
292215516c77SSepherosa Ziehau }
292315516c77SSepherosa Ziehau 
2924dc13fee6SSepherosa Ziehau static __inline int
2925dc13fee6SSepherosa Ziehau hn_flush_txagg(struct ifnet *ifp, struct hn_tx_ring *txr)
2926dc13fee6SSepherosa Ziehau {
2927dc13fee6SSepherosa Ziehau 	struct hn_txdesc *txd;
2928dc13fee6SSepherosa Ziehau 	struct mbuf *m;
2929dc13fee6SSepherosa Ziehau 	int error, pkts;
2930dc13fee6SSepherosa Ziehau 
2931dc13fee6SSepherosa Ziehau 	txd = txr->hn_agg_txd;
2932dc13fee6SSepherosa Ziehau 	KASSERT(txd != NULL, ("no aggregate txdesc"));
2933dc13fee6SSepherosa Ziehau 
2934dc13fee6SSepherosa Ziehau 	/*
2935dc13fee6SSepherosa Ziehau 	 * Since hn_txpkt() will reset this temporary stat, save
2936dc13fee6SSepherosa Ziehau 	 * it now, so that oerrors can be updated properly, if
2937dc13fee6SSepherosa Ziehau 	 * hn_txpkt() ever fails.
2938dc13fee6SSepherosa Ziehau 	 */
2939dc13fee6SSepherosa Ziehau 	pkts = txr->hn_stat_pkts;
2940dc13fee6SSepherosa Ziehau 
2941dc13fee6SSepherosa Ziehau 	/*
2942dc13fee6SSepherosa Ziehau 	 * Since txd's mbuf will _not_ be freed upon hn_txpkt()
2943dc13fee6SSepherosa Ziehau 	 * failure, save it for later freeing, if hn_txpkt() ever
2944dc13fee6SSepherosa Ziehau 	 * fails.
2945dc13fee6SSepherosa Ziehau 	 */
2946dc13fee6SSepherosa Ziehau 	m = txd->m;
2947dc13fee6SSepherosa Ziehau 	error = hn_txpkt(ifp, txr, txd);
2948dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
2949dc13fee6SSepherosa Ziehau 		/* txd is freed, but m is not. */
2950dc13fee6SSepherosa Ziehau 		m_freem(m);
2951dc13fee6SSepherosa Ziehau 
2952dc13fee6SSepherosa Ziehau 		txr->hn_flush_failed++;
2953dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, pkts);
2954dc13fee6SSepherosa Ziehau 	}
2955dc13fee6SSepherosa Ziehau 
2956dc13fee6SSepherosa Ziehau 	/* Reset all aggregation states. */
2957dc13fee6SSepherosa Ziehau 	txr->hn_agg_txd = NULL;
2958dc13fee6SSepherosa Ziehau 	txr->hn_agg_szleft = 0;
2959dc13fee6SSepherosa Ziehau 	txr->hn_agg_pktleft = 0;
2960dc13fee6SSepherosa Ziehau 	txr->hn_agg_prevpkt = NULL;
2961dc13fee6SSepherosa Ziehau 
2962dc13fee6SSepherosa Ziehau 	return (error);
2963dc13fee6SSepherosa Ziehau }
2964dc13fee6SSepherosa Ziehau 
2965dc13fee6SSepherosa Ziehau static void *
2966dc13fee6SSepherosa Ziehau hn_try_txagg(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
2967dc13fee6SSepherosa Ziehau     int pktsize)
2968dc13fee6SSepherosa Ziehau {
2969dc13fee6SSepherosa Ziehau 	void *chim;
2970dc13fee6SSepherosa Ziehau 
2971dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL) {
2972dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft >= 1 && txr->hn_agg_szleft > pktsize) {
2973dc13fee6SSepherosa Ziehau 			struct hn_txdesc *agg_txd = txr->hn_agg_txd;
2974dc13fee6SSepherosa Ziehau 			struct rndis_packet_msg *pkt = txr->hn_agg_prevpkt;
2975dc13fee6SSepherosa Ziehau 			int olen;
2976dc13fee6SSepherosa Ziehau 
2977dc13fee6SSepherosa Ziehau 			/*
2978dc13fee6SSepherosa Ziehau 			 * Update the previous RNDIS packet's total length,
2979dc13fee6SSepherosa Ziehau 			 * it can be increased due to the mandatory alignment
2980dc13fee6SSepherosa Ziehau 			 * padding for this RNDIS packet.  And update the
2981dc13fee6SSepherosa Ziehau 			 * aggregating txdesc's chimney sending buffer size
2982dc13fee6SSepherosa Ziehau 			 * accordingly.
2983dc13fee6SSepherosa Ziehau 			 *
2984dc13fee6SSepherosa Ziehau 			 * XXX
2985dc13fee6SSepherosa Ziehau 			 * Zero-out the padding, as required by the RNDIS spec.
2986dc13fee6SSepherosa Ziehau 			 */
2987dc13fee6SSepherosa Ziehau 			olen = pkt->rm_len;
2988dc13fee6SSepherosa Ziehau 			pkt->rm_len = roundup2(olen, txr->hn_agg_align);
2989dc13fee6SSepherosa Ziehau 			agg_txd->chim_size += pkt->rm_len - olen;
2990dc13fee6SSepherosa Ziehau 
2991dc13fee6SSepherosa Ziehau 			/* Link this txdesc to the parent. */
2992dc13fee6SSepherosa Ziehau 			hn_txdesc_agg(agg_txd, txd);
2993dc13fee6SSepherosa Ziehau 
2994dc13fee6SSepherosa Ziehau 			chim = (uint8_t *)pkt + pkt->rm_len;
2995dc13fee6SSepherosa Ziehau 			/* Save the current packet for later fixup. */
2996dc13fee6SSepherosa Ziehau 			txr->hn_agg_prevpkt = chim;
2997dc13fee6SSepherosa Ziehau 
2998dc13fee6SSepherosa Ziehau 			txr->hn_agg_pktleft--;
2999dc13fee6SSepherosa Ziehau 			txr->hn_agg_szleft -= pktsize;
3000dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_szleft <=
3001dc13fee6SSepherosa Ziehau 			    HN_PKTSIZE_MIN(txr->hn_agg_align)) {
3002dc13fee6SSepherosa Ziehau 				/*
3003dc13fee6SSepherosa Ziehau 				 * Probably can't aggregate more packets,
3004dc13fee6SSepherosa Ziehau 				 * flush this aggregating txdesc proactively.
3005dc13fee6SSepherosa Ziehau 				 */
3006dc13fee6SSepherosa Ziehau 				txr->hn_agg_pktleft = 0;
3007dc13fee6SSepherosa Ziehau 			}
3008dc13fee6SSepherosa Ziehau 			/* Done! */
3009dc13fee6SSepherosa Ziehau 			return (chim);
3010dc13fee6SSepherosa Ziehau 		}
3011dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
3012dc13fee6SSepherosa Ziehau 	}
3013dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
3014dc13fee6SSepherosa Ziehau 
3015dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney_tried++;
3016dc13fee6SSepherosa Ziehau 	txd->chim_index = hn_chim_alloc(txr->hn_sc);
3017dc13fee6SSepherosa Ziehau 	if (txd->chim_index == HN_NVS_CHIM_IDX_INVALID)
3018dc13fee6SSepherosa Ziehau 		return (NULL);
3019dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney++;
3020dc13fee6SSepherosa Ziehau 
3021dc13fee6SSepherosa Ziehau 	chim = txr->hn_sc->hn_chim +
3022dc13fee6SSepherosa Ziehau 	    (txd->chim_index * txr->hn_sc->hn_chim_szmax);
3023dc13fee6SSepherosa Ziehau 
3024dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_pktmax > 1 &&
3025dc13fee6SSepherosa Ziehau 	    txr->hn_agg_szmax > pktsize + HN_PKTSIZE_MIN(txr->hn_agg_align)) {
3026dc13fee6SSepherosa Ziehau 		txr->hn_agg_txd = txd;
3027dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktleft = txr->hn_agg_pktmax - 1;
3028dc13fee6SSepherosa Ziehau 		txr->hn_agg_szleft = txr->hn_agg_szmax - pktsize;
3029dc13fee6SSepherosa Ziehau 		txr->hn_agg_prevpkt = chim;
3030dc13fee6SSepherosa Ziehau 	}
3031dc13fee6SSepherosa Ziehau 	return (chim);
3032dc13fee6SSepherosa Ziehau }
3033dc13fee6SSepherosa Ziehau 
303415516c77SSepherosa Ziehau /*
303515516c77SSepherosa Ziehau  * NOTE:
303615516c77SSepherosa Ziehau  * If this function fails, then both txd and m_head0 will be freed.
303715516c77SSepherosa Ziehau  */
303815516c77SSepherosa Ziehau static int
3039dc13fee6SSepherosa Ziehau hn_encap(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
3040dc13fee6SSepherosa Ziehau     struct mbuf **m_head0)
304115516c77SSepherosa Ziehau {
304215516c77SSepherosa Ziehau 	bus_dma_segment_t segs[HN_TX_DATA_SEGCNT_MAX];
304315516c77SSepherosa Ziehau 	int error, nsegs, i;
304415516c77SSepherosa Ziehau 	struct mbuf *m_head = *m_head0;
304515516c77SSepherosa Ziehau 	struct rndis_packet_msg *pkt;
304615516c77SSepherosa Ziehau 	uint32_t *pi_data;
30478966e5d5SSepherosa Ziehau 	void *chim = NULL;
3048dc13fee6SSepherosa Ziehau 	int pkt_hlen, pkt_size;
304915516c77SSepherosa Ziehau 
305015516c77SSepherosa Ziehau 	pkt = txd->rndis_pkt;
3051dc13fee6SSepherosa Ziehau 	pkt_size = HN_PKTSIZE(m_head, txr->hn_agg_align);
3052dc13fee6SSepherosa Ziehau 	if (pkt_size < txr->hn_chim_size) {
3053dc13fee6SSepherosa Ziehau 		chim = hn_try_txagg(ifp, txr, txd, pkt_size);
3054dc13fee6SSepherosa Ziehau 		if (chim != NULL)
30558966e5d5SSepherosa Ziehau 			pkt = chim;
3056dc13fee6SSepherosa Ziehau 	} else {
3057dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL)
3058dc13fee6SSepherosa Ziehau 			hn_flush_txagg(ifp, txr);
30598966e5d5SSepherosa Ziehau 	}
30608966e5d5SSepherosa Ziehau 
306115516c77SSepherosa Ziehau 	pkt->rm_type = REMOTE_NDIS_PACKET_MSG;
30628fe90f73SSepherosa Ziehau 	pkt->rm_len = m_head->m_pkthdr.len;
30639130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = 0;
306415516c77SSepherosa Ziehau 	pkt->rm_datalen = m_head->m_pkthdr.len;
3065dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataoffset = 0;
3066dc13fee6SSepherosa Ziehau 	pkt->rm_oobdatalen = 0;
3067dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataelements = 0;
306815516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = sizeof(*pkt);
306915516c77SSepherosa Ziehau 	pkt->rm_pktinfolen = 0;
3070dc13fee6SSepherosa Ziehau 	pkt->rm_vchandle = 0;
3071dc13fee6SSepherosa Ziehau 	pkt->rm_reserved = 0;
307215516c77SSepherosa Ziehau 
307315516c77SSepherosa Ziehau 	if (txr->hn_tx_flags & HN_TX_FLAG_HASHVAL) {
307415516c77SSepherosa Ziehau 		/*
307515516c77SSepherosa Ziehau 		 * Set the hash value for this packet, so that the host could
307615516c77SSepherosa Ziehau 		 * dispatch the TX done event for this packet back to this TX
307715516c77SSepherosa Ziehau 		 * ring's channel.
307815516c77SSepherosa Ziehau 		 */
307915516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
308015516c77SSepherosa Ziehau 		    HN_NDIS_HASH_VALUE_SIZE, HN_NDIS_PKTINFO_TYPE_HASHVAL);
308115516c77SSepherosa Ziehau 		*pi_data = txr->hn_tx_idx;
308215516c77SSepherosa Ziehau 	}
308315516c77SSepherosa Ziehau 
308415516c77SSepherosa Ziehau 	if (m_head->m_flags & M_VLANTAG) {
308515516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
308615516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_SIZE, NDIS_PKTINFO_TYPE_VLAN);
308715516c77SSepherosa Ziehau 		*pi_data = NDIS_VLAN_INFO_MAKE(
308815516c77SSepherosa Ziehau 		    EVL_VLANOFTAG(m_head->m_pkthdr.ether_vtag),
308915516c77SSepherosa Ziehau 		    EVL_PRIOFTAG(m_head->m_pkthdr.ether_vtag),
309015516c77SSepherosa Ziehau 		    EVL_CFIOFTAG(m_head->m_pkthdr.ether_vtag));
309115516c77SSepherosa Ziehau 	}
309215516c77SSepherosa Ziehau 
309315516c77SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
309415516c77SSepherosa Ziehau #if defined(INET6) || defined(INET)
309515516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
309615516c77SSepherosa Ziehau 		    NDIS_LSO2_INFO_SIZE, NDIS_PKTINFO_TYPE_LSO);
309715516c77SSepherosa Ziehau #ifdef INET
309815516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
3099c49d47daSSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV4(
3100c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen,
310115516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
310215516c77SSepherosa Ziehau 		}
310315516c77SSepherosa Ziehau #endif
310415516c77SSepherosa Ziehau #if defined(INET6) && defined(INET)
310515516c77SSepherosa Ziehau 		else
310615516c77SSepherosa Ziehau #endif
310715516c77SSepherosa Ziehau #ifdef INET6
310815516c77SSepherosa Ziehau 		{
3109c49d47daSSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV6(
3110c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen,
311115516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
311215516c77SSepherosa Ziehau 		}
311315516c77SSepherosa Ziehau #endif
311415516c77SSepherosa Ziehau #endif	/* INET6 || INET */
311515516c77SSepherosa Ziehau 	} else if (m_head->m_pkthdr.csum_flags & txr->hn_csum_assist) {
311615516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
311715516c77SSepherosa Ziehau 		    NDIS_TXCSUM_INFO_SIZE, NDIS_PKTINFO_TYPE_CSUM);
311815516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
311915516c77SSepherosa Ziehau 		    (CSUM_IP6_TCP | CSUM_IP6_UDP)) {
312015516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV6;
312115516c77SSepherosa Ziehau 		} else {
312215516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV4;
312315516c77SSepherosa Ziehau 			if (m_head->m_pkthdr.csum_flags & CSUM_IP)
312415516c77SSepherosa Ziehau 				*pi_data |= NDIS_TXCSUM_INFO_IPCS;
312515516c77SSepherosa Ziehau 		}
312615516c77SSepherosa Ziehau 
3127c49d47daSSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
3128c49d47daSSepherosa Ziehau 		    (CSUM_IP_TCP | CSUM_IP6_TCP)) {
3129c49d47daSSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_MKTCPCS(
3130c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen);
3131c49d47daSSepherosa Ziehau 		} else if (m_head->m_pkthdr.csum_flags &
3132c49d47daSSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP6_UDP)) {
3133c49d47daSSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_MKUDPCS(
3134c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen);
3135c49d47daSSepherosa Ziehau 		}
313615516c77SSepherosa Ziehau 	}
313715516c77SSepherosa Ziehau 
3138dc13fee6SSepherosa Ziehau 	pkt_hlen = pkt->rm_pktinfooffset + pkt->rm_pktinfolen;
31398fe90f73SSepherosa Ziehau 	/* Fixup RNDIS packet message total length */
31408fe90f73SSepherosa Ziehau 	pkt->rm_len += pkt_hlen;
314115516c77SSepherosa Ziehau 	/* Convert RNDIS packet message offsets */
31429130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = hn_rndis_pktmsg_offset(pkt_hlen);
314315516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = hn_rndis_pktmsg_offset(pkt->rm_pktinfooffset);
314415516c77SSepherosa Ziehau 
314515516c77SSepherosa Ziehau 	/*
31468966e5d5SSepherosa Ziehau 	 * Fast path: Chimney sending.
314715516c77SSepherosa Ziehau 	 */
31488966e5d5SSepherosa Ziehau 	if (chim != NULL) {
3149dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tgt_txd = txd;
3150dc13fee6SSepherosa Ziehau 
3151dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL) {
3152dc13fee6SSepherosa Ziehau 			tgt_txd = txr->hn_agg_txd;
3153dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
3154dc13fee6SSepherosa Ziehau 			*m_head0 = NULL;
3155dc13fee6SSepherosa Ziehau #endif
3156dc13fee6SSepherosa Ziehau 		}
3157dc13fee6SSepherosa Ziehau 
3158dc13fee6SSepherosa Ziehau 		KASSERT(pkt == chim,
3159dc13fee6SSepherosa Ziehau 		    ("RNDIS pkt not in chimney sending buffer"));
3160dc13fee6SSepherosa Ziehau 		KASSERT(tgt_txd->chim_index != HN_NVS_CHIM_IDX_INVALID,
3161dc13fee6SSepherosa Ziehau 		    ("chimney sending buffer is not used"));
3162dc13fee6SSepherosa Ziehau 		tgt_txd->chim_size += pkt->rm_len;
316315516c77SSepherosa Ziehau 
31648966e5d5SSepherosa Ziehau 		m_copydata(m_head, 0, m_head->m_pkthdr.len,
3165dc13fee6SSepherosa Ziehau 		    ((uint8_t *)chim) + pkt_hlen);
316615516c77SSepherosa Ziehau 
316715516c77SSepherosa Ziehau 		txr->hn_gpa_cnt = 0;
316815516c77SSepherosa Ziehau 		txr->hn_sendpkt = hn_txpkt_chim;
316915516c77SSepherosa Ziehau 		goto done;
317015516c77SSepherosa Ziehau 	}
3171dc13fee6SSepherosa Ziehau 
3172dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("aggregating sglist txdesc"));
31738966e5d5SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
31748966e5d5SSepherosa Ziehau 	    ("chimney buffer is used"));
31758966e5d5SSepherosa Ziehau 	KASSERT(pkt == txd->rndis_pkt, ("RNDIS pkt not in txdesc"));
317615516c77SSepherosa Ziehau 
317715516c77SSepherosa Ziehau 	error = hn_txdesc_dmamap_load(txr, txd, &m_head, segs, &nsegs);
3178dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
317915516c77SSepherosa Ziehau 		int freed;
318015516c77SSepherosa Ziehau 
318115516c77SSepherosa Ziehau 		/*
318215516c77SSepherosa Ziehau 		 * This mbuf is not linked w/ the txd yet, so free it now.
318315516c77SSepherosa Ziehau 		 */
318415516c77SSepherosa Ziehau 		m_freem(m_head);
318515516c77SSepherosa Ziehau 		*m_head0 = NULL;
318615516c77SSepherosa Ziehau 
318715516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
318815516c77SSepherosa Ziehau 		KASSERT(freed != 0,
318915516c77SSepherosa Ziehau 		    ("fail to free txd upon txdma error"));
319015516c77SSepherosa Ziehau 
319115516c77SSepherosa Ziehau 		txr->hn_txdma_failed++;
3192dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
319315516c77SSepherosa Ziehau 		return error;
319415516c77SSepherosa Ziehau 	}
319515516c77SSepherosa Ziehau 	*m_head0 = m_head;
319615516c77SSepherosa Ziehau 
319715516c77SSepherosa Ziehau 	/* +1 RNDIS packet message */
319815516c77SSepherosa Ziehau 	txr->hn_gpa_cnt = nsegs + 1;
319915516c77SSepherosa Ziehau 
320015516c77SSepherosa Ziehau 	/* send packet with page buffer */
320115516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_page = atop(txd->rndis_pkt_paddr);
320215516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_ofs = txd->rndis_pkt_paddr & PAGE_MASK;
3203dc13fee6SSepherosa Ziehau 	txr->hn_gpa[0].gpa_len = pkt_hlen;
320415516c77SSepherosa Ziehau 
320515516c77SSepherosa Ziehau 	/*
320615516c77SSepherosa Ziehau 	 * Fill the page buffers with mbuf info after the page
320715516c77SSepherosa Ziehau 	 * buffer for RNDIS packet message.
320815516c77SSepherosa Ziehau 	 */
320915516c77SSepherosa Ziehau 	for (i = 0; i < nsegs; ++i) {
321015516c77SSepherosa Ziehau 		struct vmbus_gpa *gpa = &txr->hn_gpa[i + 1];
321115516c77SSepherosa Ziehau 
321215516c77SSepherosa Ziehau 		gpa->gpa_page = atop(segs[i].ds_addr);
321315516c77SSepherosa Ziehau 		gpa->gpa_ofs = segs[i].ds_addr & PAGE_MASK;
321415516c77SSepherosa Ziehau 		gpa->gpa_len = segs[i].ds_len;
321515516c77SSepherosa Ziehau 	}
321615516c77SSepherosa Ziehau 
321715516c77SSepherosa Ziehau 	txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
321815516c77SSepherosa Ziehau 	txd->chim_size = 0;
321915516c77SSepherosa Ziehau 	txr->hn_sendpkt = hn_txpkt_sglist;
322015516c77SSepherosa Ziehau done:
322115516c77SSepherosa Ziehau 	txd->m = m_head;
322215516c77SSepherosa Ziehau 
322315516c77SSepherosa Ziehau 	/* Set the completion routine */
322415516c77SSepherosa Ziehau 	hn_nvs_sendctx_init(&txd->send_ctx, hn_txpkt_done, txd);
322515516c77SSepherosa Ziehau 
3226dc13fee6SSepherosa Ziehau 	/* Update temporary stats for later use. */
3227dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts++;
3228dc13fee6SSepherosa Ziehau 	txr->hn_stat_size += m_head->m_pkthdr.len;
3229dc13fee6SSepherosa Ziehau 	if (m_head->m_flags & M_MCAST)
3230dc13fee6SSepherosa Ziehau 		txr->hn_stat_mcasts++;
3231dc13fee6SSepherosa Ziehau 
323215516c77SSepherosa Ziehau 	return 0;
323315516c77SSepherosa Ziehau }
323415516c77SSepherosa Ziehau 
323515516c77SSepherosa Ziehau /*
323615516c77SSepherosa Ziehau  * NOTE:
323715516c77SSepherosa Ziehau  * If this function fails, then txd will be freed, but the mbuf
323815516c77SSepherosa Ziehau  * associated w/ the txd will _not_ be freed.
323915516c77SSepherosa Ziehau  */
324015516c77SSepherosa Ziehau static int
324115516c77SSepherosa Ziehau hn_txpkt(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd)
324215516c77SSepherosa Ziehau {
32438e7d3136SSepherosa Ziehau 	int error, send_failed = 0, has_bpf;
324415516c77SSepherosa Ziehau 
324515516c77SSepherosa Ziehau again:
32468e7d3136SSepherosa Ziehau 	has_bpf = bpf_peers_present(ifp->if_bpf);
32478e7d3136SSepherosa Ziehau 	if (has_bpf) {
324815516c77SSepherosa Ziehau 		/*
32498e7d3136SSepherosa Ziehau 		 * Make sure that this txd and any aggregated txds are not
32508e7d3136SSepherosa Ziehau 		 * freed before ETHER_BPF_MTAP.
325115516c77SSepherosa Ziehau 		 */
325215516c77SSepherosa Ziehau 		hn_txdesc_hold(txd);
32538e7d3136SSepherosa Ziehau 	}
325415516c77SSepherosa Ziehau 	error = txr->hn_sendpkt(txr, txd);
325515516c77SSepherosa Ziehau 	if (!error) {
32568e7d3136SSepherosa Ziehau 		if (has_bpf) {
3257dc13fee6SSepherosa Ziehau 			const struct hn_txdesc *tmp_txd;
3258dc13fee6SSepherosa Ziehau 
325915516c77SSepherosa Ziehau 			ETHER_BPF_MTAP(ifp, txd->m);
3260dc13fee6SSepherosa Ziehau 			STAILQ_FOREACH(tmp_txd, &txd->agg_list, agg_link)
3261dc13fee6SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, tmp_txd->m);
3262dc13fee6SSepherosa Ziehau 		}
3263dc13fee6SSepherosa Ziehau 
3264dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, txr->hn_stat_pkts);
326523bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
326623bf9e15SSepherosa Ziehau 		if (!hn_use_if_start)
326723bf9e15SSepherosa Ziehau #endif
326823bf9e15SSepherosa Ziehau 		{
326915516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OBYTES,
3270dc13fee6SSepherosa Ziehau 			    txr->hn_stat_size);
3271dc13fee6SSepherosa Ziehau 			if (txr->hn_stat_mcasts != 0) {
3272dc13fee6SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OMCASTS,
3273dc13fee6SSepherosa Ziehau 				    txr->hn_stat_mcasts);
327415516c77SSepherosa Ziehau 			}
3275dc13fee6SSepherosa Ziehau 		}
3276dc13fee6SSepherosa Ziehau 		txr->hn_pkts += txr->hn_stat_pkts;
3277dc13fee6SSepherosa Ziehau 		txr->hn_sends++;
327815516c77SSepherosa Ziehau 	}
32798e7d3136SSepherosa Ziehau 	if (has_bpf)
328015516c77SSepherosa Ziehau 		hn_txdesc_put(txr, txd);
328115516c77SSepherosa Ziehau 
328215516c77SSepherosa Ziehau 	if (__predict_false(error)) {
328315516c77SSepherosa Ziehau 		int freed;
328415516c77SSepherosa Ziehau 
328515516c77SSepherosa Ziehau 		/*
328615516c77SSepherosa Ziehau 		 * This should "really rarely" happen.
328715516c77SSepherosa Ziehau 		 *
328815516c77SSepherosa Ziehau 		 * XXX Too many RX to be acked or too many sideband
328915516c77SSepherosa Ziehau 		 * commands to run?  Ask netvsc_channel_rollup()
329015516c77SSepherosa Ziehau 		 * to kick start later.
329115516c77SSepherosa Ziehau 		 */
329215516c77SSepherosa Ziehau 		txr->hn_has_txeof = 1;
329315516c77SSepherosa Ziehau 		if (!send_failed) {
329415516c77SSepherosa Ziehau 			txr->hn_send_failed++;
329515516c77SSepherosa Ziehau 			send_failed = 1;
329615516c77SSepherosa Ziehau 			/*
329715516c77SSepherosa Ziehau 			 * Try sending again after set hn_has_txeof;
329815516c77SSepherosa Ziehau 			 * in case that we missed the last
329915516c77SSepherosa Ziehau 			 * netvsc_channel_rollup().
330015516c77SSepherosa Ziehau 			 */
330115516c77SSepherosa Ziehau 			goto again;
330215516c77SSepherosa Ziehau 		}
330315516c77SSepherosa Ziehau 		if_printf(ifp, "send failed\n");
330415516c77SSepherosa Ziehau 
330515516c77SSepherosa Ziehau 		/*
330615516c77SSepherosa Ziehau 		 * Caller will perform further processing on the
330715516c77SSepherosa Ziehau 		 * associated mbuf, so don't free it in hn_txdesc_put();
330815516c77SSepherosa Ziehau 		 * only unload it from the DMA map in hn_txdesc_put(),
330915516c77SSepherosa Ziehau 		 * if it was loaded.
331015516c77SSepherosa Ziehau 		 */
331115516c77SSepherosa Ziehau 		txd->m = NULL;
331215516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
331315516c77SSepherosa Ziehau 		KASSERT(freed != 0,
331415516c77SSepherosa Ziehau 		    ("fail to free txd upon send error"));
331515516c77SSepherosa Ziehau 
331615516c77SSepherosa Ziehau 		txr->hn_send_failed++;
331715516c77SSepherosa Ziehau 	}
3318dc13fee6SSepherosa Ziehau 
3319dc13fee6SSepherosa Ziehau 	/* Reset temporary stats, after this sending is done. */
3320dc13fee6SSepherosa Ziehau 	txr->hn_stat_size = 0;
3321dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts = 0;
3322dc13fee6SSepherosa Ziehau 	txr->hn_stat_mcasts = 0;
3323dc13fee6SSepherosa Ziehau 
3324dc13fee6SSepherosa Ziehau 	return (error);
332515516c77SSepherosa Ziehau }
332615516c77SSepherosa Ziehau 
332715516c77SSepherosa Ziehau /*
332815516c77SSepherosa Ziehau  * Append the specified data to the indicated mbuf chain,
332915516c77SSepherosa Ziehau  * Extend the mbuf chain if the new data does not fit in
333015516c77SSepherosa Ziehau  * existing space.
333115516c77SSepherosa Ziehau  *
333215516c77SSepherosa Ziehau  * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
333315516c77SSepherosa Ziehau  * There should be an equivalent in the kernel mbuf code,
333415516c77SSepherosa Ziehau  * but there does not appear to be one yet.
333515516c77SSepherosa Ziehau  *
333615516c77SSepherosa Ziehau  * Differs from m_append() in that additional mbufs are
333715516c77SSepherosa Ziehau  * allocated with cluster size MJUMPAGESIZE, and filled
333815516c77SSepherosa Ziehau  * accordingly.
333915516c77SSepherosa Ziehau  *
334015516c77SSepherosa Ziehau  * Return 1 if able to complete the job; otherwise 0.
334115516c77SSepherosa Ziehau  */
334215516c77SSepherosa Ziehau static int
334315516c77SSepherosa Ziehau hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
334415516c77SSepherosa Ziehau {
334515516c77SSepherosa Ziehau 	struct mbuf *m, *n;
334615516c77SSepherosa Ziehau 	int remainder, space;
334715516c77SSepherosa Ziehau 
334815516c77SSepherosa Ziehau 	for (m = m0; m->m_next != NULL; m = m->m_next)
334915516c77SSepherosa Ziehau 		;
335015516c77SSepherosa Ziehau 	remainder = len;
335115516c77SSepherosa Ziehau 	space = M_TRAILINGSPACE(m);
335215516c77SSepherosa Ziehau 	if (space > 0) {
335315516c77SSepherosa Ziehau 		/*
335415516c77SSepherosa Ziehau 		 * Copy into available space.
335515516c77SSepherosa Ziehau 		 */
335615516c77SSepherosa Ziehau 		if (space > remainder)
335715516c77SSepherosa Ziehau 			space = remainder;
335815516c77SSepherosa Ziehau 		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
335915516c77SSepherosa Ziehau 		m->m_len += space;
336015516c77SSepherosa Ziehau 		cp += space;
336115516c77SSepherosa Ziehau 		remainder -= space;
336215516c77SSepherosa Ziehau 	}
336315516c77SSepherosa Ziehau 	while (remainder > 0) {
336415516c77SSepherosa Ziehau 		/*
336515516c77SSepherosa Ziehau 		 * Allocate a new mbuf; could check space
336615516c77SSepherosa Ziehau 		 * and allocate a cluster instead.
336715516c77SSepherosa Ziehau 		 */
336815516c77SSepherosa Ziehau 		n = m_getjcl(M_NOWAIT, m->m_type, 0, MJUMPAGESIZE);
336915516c77SSepherosa Ziehau 		if (n == NULL)
337015516c77SSepherosa Ziehau 			break;
337115516c77SSepherosa Ziehau 		n->m_len = min(MJUMPAGESIZE, remainder);
337215516c77SSepherosa Ziehau 		bcopy(cp, mtod(n, caddr_t), n->m_len);
337315516c77SSepherosa Ziehau 		cp += n->m_len;
337415516c77SSepherosa Ziehau 		remainder -= n->m_len;
337515516c77SSepherosa Ziehau 		m->m_next = n;
337615516c77SSepherosa Ziehau 		m = n;
337715516c77SSepherosa Ziehau 	}
337815516c77SSepherosa Ziehau 	if (m0->m_flags & M_PKTHDR)
337915516c77SSepherosa Ziehau 		m0->m_pkthdr.len += len - remainder;
338015516c77SSepherosa Ziehau 
338115516c77SSepherosa Ziehau 	return (remainder == 0);
338215516c77SSepherosa Ziehau }
338315516c77SSepherosa Ziehau 
338415516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
338515516c77SSepherosa Ziehau static __inline int
338615516c77SSepherosa Ziehau hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m)
338715516c77SSepherosa Ziehau {
338815516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
338915516c77SSepherosa Ziehau 	if (hn_lro_mbufq_depth) {
339015516c77SSepherosa Ziehau 		tcp_lro_queue_mbuf(lc, m);
339115516c77SSepherosa Ziehau 		return 0;
339215516c77SSepherosa Ziehau 	}
339315516c77SSepherosa Ziehau #endif
339415516c77SSepherosa Ziehau 	return tcp_lro_rx(lc, m, 0);
339515516c77SSepherosa Ziehau }
339615516c77SSepherosa Ziehau #endif
339715516c77SSepherosa Ziehau 
339815516c77SSepherosa Ziehau static int
339915516c77SSepherosa Ziehau hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
340015516c77SSepherosa Ziehau     const struct hn_rxinfo *info)
340115516c77SSepherosa Ziehau {
3402a97fff19SSepherosa Ziehau 	struct ifnet *ifp, *hn_ifp = rxr->hn_ifp;
340315516c77SSepherosa Ziehau 	struct mbuf *m_new;
3404642ec226SSepherosa Ziehau 	int size, do_lro = 0, do_csum = 1, is_vf = 0;
3405642ec226SSepherosa Ziehau 	int hash_type = M_HASHTYPE_NONE;
3406db76829bSSepherosa Ziehau 	int l3proto = ETHERTYPE_MAX, l4proto = IPPROTO_DONE;
340715516c77SSepherosa Ziehau 
3408642ec226SSepherosa Ziehau 	ifp = hn_ifp;
3409642ec226SSepherosa Ziehau 	if (rxr->hn_rxvf_ifp != NULL) {
3410a97fff19SSepherosa Ziehau 		/*
3411642ec226SSepherosa Ziehau 		 * Non-transparent mode VF; pretend this packet is from
3412642ec226SSepherosa Ziehau 		 * the VF.
3413a97fff19SSepherosa Ziehau 		 */
3414642ec226SSepherosa Ziehau 		ifp = rxr->hn_rxvf_ifp;
3415642ec226SSepherosa Ziehau 		is_vf = 1;
3416642ec226SSepherosa Ziehau 	} else if (rxr->hn_rx_flags & HN_RX_FLAG_XPNT_VF) {
3417642ec226SSepherosa Ziehau 		/* Transparent mode VF. */
3418642ec226SSepherosa Ziehau 		is_vf = 1;
3419642ec226SSepherosa Ziehau 	}
34205bdfd3fdSDexuan Cui 
3421b3b75d9cSSepherosa Ziehau 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
3422b3b75d9cSSepherosa Ziehau 		/*
3423b3b75d9cSSepherosa Ziehau 		 * NOTE:
3424b3b75d9cSSepherosa Ziehau 		 * See the NOTE of hn_rndis_init_fixat().  This
3425b3b75d9cSSepherosa Ziehau 		 * function can be reached, immediately after the
3426b3b75d9cSSepherosa Ziehau 		 * RNDIS is initialized but before the ifnet is
3427b3b75d9cSSepherosa Ziehau 		 * setup on the hn_attach() path; drop the unexpected
3428b3b75d9cSSepherosa Ziehau 		 * packets.
3429b3b75d9cSSepherosa Ziehau 		 */
3430b3b75d9cSSepherosa Ziehau 		return (0);
3431b3b75d9cSSepherosa Ziehau 	}
3432b3b75d9cSSepherosa Ziehau 
3433a97fff19SSepherosa Ziehau 	if (__predict_false(dlen < ETHER_HDR_LEN)) {
3434a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IERRORS, 1);
3435a97fff19SSepherosa Ziehau 		return (0);
3436a97fff19SSepherosa Ziehau 	}
3437a97fff19SSepherosa Ziehau 
3438c927d681SDexuan Cui 	if (dlen <= MHLEN) {
343915516c77SSepherosa Ziehau 		m_new = m_gethdr(M_NOWAIT, MT_DATA);
344015516c77SSepherosa Ziehau 		if (m_new == NULL) {
3441a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
344215516c77SSepherosa Ziehau 			return (0);
344315516c77SSepherosa Ziehau 		}
344415516c77SSepherosa Ziehau 		memcpy(mtod(m_new, void *), data, dlen);
344515516c77SSepherosa Ziehau 		m_new->m_pkthdr.len = m_new->m_len = dlen;
344615516c77SSepherosa Ziehau 		rxr->hn_small_pkts++;
344715516c77SSepherosa Ziehau 	} else {
344815516c77SSepherosa Ziehau 		/*
344915516c77SSepherosa Ziehau 		 * Get an mbuf with a cluster.  For packets 2K or less,
345015516c77SSepherosa Ziehau 		 * get a standard 2K cluster.  For anything larger, get a
345115516c77SSepherosa Ziehau 		 * 4K cluster.  Any buffers larger than 4K can cause problems
345215516c77SSepherosa Ziehau 		 * if looped around to the Hyper-V TX channel, so avoid them.
345315516c77SSepherosa Ziehau 		 */
345415516c77SSepherosa Ziehau 		size = MCLBYTES;
345515516c77SSepherosa Ziehau 		if (dlen > MCLBYTES) {
345615516c77SSepherosa Ziehau 			/* 4096 */
345715516c77SSepherosa Ziehau 			size = MJUMPAGESIZE;
345815516c77SSepherosa Ziehau 		}
345915516c77SSepherosa Ziehau 
346015516c77SSepherosa Ziehau 		m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size);
346115516c77SSepherosa Ziehau 		if (m_new == NULL) {
3462a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
346315516c77SSepherosa Ziehau 			return (0);
346415516c77SSepherosa Ziehau 		}
346515516c77SSepherosa Ziehau 
346615516c77SSepherosa Ziehau 		hv_m_append(m_new, dlen, data);
346715516c77SSepherosa Ziehau 	}
346815516c77SSepherosa Ziehau 	m_new->m_pkthdr.rcvif = ifp;
346915516c77SSepherosa Ziehau 
3470a97fff19SSepherosa Ziehau 	if (__predict_false((hn_ifp->if_capenable & IFCAP_RXCSUM) == 0))
347115516c77SSepherosa Ziehau 		do_csum = 0;
347215516c77SSepherosa Ziehau 
347315516c77SSepherosa Ziehau 	/* receive side checksum offload */
347415516c77SSepherosa Ziehau 	if (info->csum_info != HN_NDIS_RXCSUM_INFO_INVALID) {
347515516c77SSepherosa Ziehau 		/* IP csum offload */
347615516c77SSepherosa Ziehau 		if ((info->csum_info & NDIS_RXCSUM_INFO_IPCS_OK) && do_csum) {
347715516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
347815516c77SSepherosa Ziehau 			    (CSUM_IP_CHECKED | CSUM_IP_VALID);
347915516c77SSepherosa Ziehau 			rxr->hn_csum_ip++;
348015516c77SSepherosa Ziehau 		}
348115516c77SSepherosa Ziehau 
348215516c77SSepherosa Ziehau 		/* TCP/UDP csum offload */
348315516c77SSepherosa Ziehau 		if ((info->csum_info & (NDIS_RXCSUM_INFO_UDPCS_OK |
348415516c77SSepherosa Ziehau 		     NDIS_RXCSUM_INFO_TCPCS_OK)) && do_csum) {
348515516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
348615516c77SSepherosa Ziehau 			    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
348715516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_data = 0xffff;
348815516c77SSepherosa Ziehau 			if (info->csum_info & NDIS_RXCSUM_INFO_TCPCS_OK)
348915516c77SSepherosa Ziehau 				rxr->hn_csum_tcp++;
349015516c77SSepherosa Ziehau 			else
349115516c77SSepherosa Ziehau 				rxr->hn_csum_udp++;
349215516c77SSepherosa Ziehau 		}
349315516c77SSepherosa Ziehau 
349415516c77SSepherosa Ziehau 		/*
349515516c77SSepherosa Ziehau 		 * XXX
349615516c77SSepherosa Ziehau 		 * As of this write (Oct 28th, 2016), host side will turn
349715516c77SSepherosa Ziehau 		 * on only TCPCS_OK and IPCS_OK even for UDP datagrams, so
349815516c77SSepherosa Ziehau 		 * the do_lro setting here is actually _not_ accurate.  We
349915516c77SSepherosa Ziehau 		 * depend on the RSS hash type check to reset do_lro.
350015516c77SSepherosa Ziehau 		 */
350115516c77SSepherosa Ziehau 		if ((info->csum_info &
350215516c77SSepherosa Ziehau 		     (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) ==
350315516c77SSepherosa Ziehau 		    (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK))
350415516c77SSepherosa Ziehau 			do_lro = 1;
350515516c77SSepherosa Ziehau 	} else {
3506db76829bSSepherosa Ziehau 		hn_rxpkt_proto(m_new, &l3proto, &l4proto);
3507db76829bSSepherosa Ziehau 		if (l3proto == ETHERTYPE_IP) {
3508db76829bSSepherosa Ziehau 			if (l4proto == IPPROTO_TCP) {
350915516c77SSepherosa Ziehau 				if (do_csum &&
351015516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
351115516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_TCP)) {
351215516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
351315516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
351415516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
351515516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
351615516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
351715516c77SSepherosa Ziehau 				}
351815516c77SSepherosa Ziehau 				do_lro = 1;
3519db76829bSSepherosa Ziehau 			} else if (l4proto == IPPROTO_UDP) {
352015516c77SSepherosa Ziehau 				if (do_csum &&
352115516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
352215516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_UDP)) {
352315516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
352415516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
352515516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
352615516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
352715516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
352815516c77SSepherosa Ziehau 				}
3529db76829bSSepherosa Ziehau 			} else if (l4proto != IPPROTO_DONE && do_csum &&
353015516c77SSepherosa Ziehau 			    (rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) {
353115516c77SSepherosa Ziehau 				rxr->hn_csum_trusted++;
353215516c77SSepherosa Ziehau 				m_new->m_pkthdr.csum_flags |=
353315516c77SSepherosa Ziehau 				    (CSUM_IP_CHECKED | CSUM_IP_VALID);
353415516c77SSepherosa Ziehau 			}
353515516c77SSepherosa Ziehau 		}
353615516c77SSepherosa Ziehau 	}
3537db76829bSSepherosa Ziehau 
353815516c77SSepherosa Ziehau 	if (info->vlan_info != HN_NDIS_VLAN_INFO_INVALID) {
353915516c77SSepherosa Ziehau 		m_new->m_pkthdr.ether_vtag = EVL_MAKETAG(
354015516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_ID(info->vlan_info),
354115516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_PRI(info->vlan_info),
354215516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_CFI(info->vlan_info));
354315516c77SSepherosa Ziehau 		m_new->m_flags |= M_VLANTAG;
354415516c77SSepherosa Ziehau 	}
354515516c77SSepherosa Ziehau 
3546a97fff19SSepherosa Ziehau 	/*
3547a97fff19SSepherosa Ziehau 	 * If VF is activated (tranparent/non-transparent mode does not
3548a97fff19SSepherosa Ziehau 	 * matter here).
3549a97fff19SSepherosa Ziehau 	 *
3550a97fff19SSepherosa Ziehau 	 * - Disable LRO
3551a97fff19SSepherosa Ziehau 	 *
3552a97fff19SSepherosa Ziehau 	 *   hn(4) will only receive broadcast packets, multicast packets,
3553a97fff19SSepherosa Ziehau 	 *   TCP SYN and SYN|ACK (in Azure), LRO is useless for these
3554a97fff19SSepherosa Ziehau 	 *   packet types.
3555a97fff19SSepherosa Ziehau 	 *
3556a97fff19SSepherosa Ziehau 	 *   For non-transparent, we definitely _cannot_ enable LRO at
3557a97fff19SSepherosa Ziehau 	 *   all, since the LRO flush will use hn(4) as the receiving
3558a97fff19SSepherosa Ziehau 	 *   interface; i.e. hn_ifp->if_input(hn_ifp, m).
3559a97fff19SSepherosa Ziehau 	 */
3560642ec226SSepherosa Ziehau 	if (is_vf)
3561642ec226SSepherosa Ziehau 		do_lro = 0;
3562a97fff19SSepherosa Ziehau 
3563642ec226SSepherosa Ziehau 	/*
3564642ec226SSepherosa Ziehau 	 * If VF is activated (tranparent/non-transparent mode does not
3565642ec226SSepherosa Ziehau 	 * matter here), do _not_ mess with unsupported hash types or
3566642ec226SSepherosa Ziehau 	 * functions.
3567642ec226SSepherosa Ziehau 	 */
356815516c77SSepherosa Ziehau 	if (info->hash_info != HN_NDIS_HASH_INFO_INVALID) {
356915516c77SSepherosa Ziehau 		rxr->hn_rss_pkts++;
357015516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = info->hash_value;
3571642ec226SSepherosa Ziehau 		if (!is_vf)
357215516c77SSepherosa Ziehau 			hash_type = M_HASHTYPE_OPAQUE_HASH;
357315516c77SSepherosa Ziehau 		if ((info->hash_info & NDIS_HASH_FUNCTION_MASK) ==
357415516c77SSepherosa Ziehau 		    NDIS_HASH_FUNCTION_TOEPLITZ) {
3575642ec226SSepherosa Ziehau 			uint32_t type = (info->hash_info & NDIS_HASH_TYPE_MASK &
3576642ec226SSepherosa Ziehau 			    rxr->hn_mbuf_hash);
357715516c77SSepherosa Ziehau 
357815516c77SSepherosa Ziehau 			/*
357915516c77SSepherosa Ziehau 			 * NOTE:
358015516c77SSepherosa Ziehau 			 * do_lro is resetted, if the hash types are not TCP
358115516c77SSepherosa Ziehau 			 * related.  See the comment in the above csum_flags
358215516c77SSepherosa Ziehau 			 * setup section.
358315516c77SSepherosa Ziehau 			 */
358415516c77SSepherosa Ziehau 			switch (type) {
358515516c77SSepherosa Ziehau 			case NDIS_HASH_IPV4:
358615516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV4;
358715516c77SSepherosa Ziehau 				do_lro = 0;
358815516c77SSepherosa Ziehau 				break;
358915516c77SSepherosa Ziehau 
359015516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV4:
359115516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV4;
3592db76829bSSepherosa Ziehau 				if (rxr->hn_rx_flags & HN_RX_FLAG_UDP_HASH) {
3593db76829bSSepherosa Ziehau 					int def_htype = M_HASHTYPE_OPAQUE_HASH;
3594db76829bSSepherosa Ziehau 
3595db76829bSSepherosa Ziehau 					if (is_vf)
3596db76829bSSepherosa Ziehau 						def_htype = M_HASHTYPE_NONE;
3597db76829bSSepherosa Ziehau 
3598db76829bSSepherosa Ziehau 					/*
3599db76829bSSepherosa Ziehau 					 * UDP 4-tuple hash is delivered as
3600db76829bSSepherosa Ziehau 					 * TCP 4-tuple hash.
3601db76829bSSepherosa Ziehau 					 */
3602db76829bSSepherosa Ziehau 					if (l3proto == ETHERTYPE_MAX) {
3603db76829bSSepherosa Ziehau 						hn_rxpkt_proto(m_new,
3604db76829bSSepherosa Ziehau 						    &l3proto, &l4proto);
3605db76829bSSepherosa Ziehau 					}
3606db76829bSSepherosa Ziehau 					if (l3proto == ETHERTYPE_IP) {
36076f12c42eSSepherosa Ziehau 						if (l4proto == IPPROTO_UDP &&
36086f12c42eSSepherosa Ziehau 						    (rxr->hn_mbuf_hash &
36096f12c42eSSepherosa Ziehau 						     NDIS_HASH_UDP_IPV4_X)) {
3610db76829bSSepherosa Ziehau 							hash_type =
3611db76829bSSepherosa Ziehau 							M_HASHTYPE_RSS_UDP_IPV4;
3612db76829bSSepherosa Ziehau 							do_lro = 0;
3613db76829bSSepherosa Ziehau 						} else if (l4proto !=
3614db76829bSSepherosa Ziehau 						    IPPROTO_TCP) {
3615db76829bSSepherosa Ziehau 							hash_type = def_htype;
3616db76829bSSepherosa Ziehau 							do_lro = 0;
3617db76829bSSepherosa Ziehau 						}
3618db76829bSSepherosa Ziehau 					} else {
3619db76829bSSepherosa Ziehau 						hash_type = def_htype;
3620db76829bSSepherosa Ziehau 						do_lro = 0;
3621db76829bSSepherosa Ziehau 					}
3622db76829bSSepherosa Ziehau 				}
362315516c77SSepherosa Ziehau 				break;
362415516c77SSepherosa Ziehau 
362515516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6:
362615516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6;
362715516c77SSepherosa Ziehau 				do_lro = 0;
362815516c77SSepherosa Ziehau 				break;
362915516c77SSepherosa Ziehau 
363015516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6_EX:
363115516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6_EX;
363215516c77SSepherosa Ziehau 				do_lro = 0;
363315516c77SSepherosa Ziehau 				break;
363415516c77SSepherosa Ziehau 
363515516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6:
363615516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6;
363715516c77SSepherosa Ziehau 				break;
363815516c77SSepherosa Ziehau 
363915516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6_EX:
364015516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6_EX;
364115516c77SSepherosa Ziehau 				break;
364215516c77SSepherosa Ziehau 			}
364315516c77SSepherosa Ziehau 		}
3644642ec226SSepherosa Ziehau 	} else if (!is_vf) {
364515516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = rxr->hn_rx_idx;
364615516c77SSepherosa Ziehau 		hash_type = M_HASHTYPE_OPAQUE;
364715516c77SSepherosa Ziehau 	}
364815516c77SSepherosa Ziehau 	M_HASHTYPE_SET(m_new, hash_type);
364915516c77SSepherosa Ziehau 
3650a97fff19SSepherosa Ziehau 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
3651a97fff19SSepherosa Ziehau 	if (hn_ifp != ifp) {
3652a97fff19SSepherosa Ziehau 		const struct ether_header *eh;
3653a97fff19SSepherosa Ziehau 
365415516c77SSepherosa Ziehau 		/*
3655a97fff19SSepherosa Ziehau 		 * Non-transparent mode VF is activated.
365615516c77SSepherosa Ziehau 		 */
365715516c77SSepherosa Ziehau 
3658a97fff19SSepherosa Ziehau 		/*
3659a97fff19SSepherosa Ziehau 		 * Allow tapping on hn(4).
3660a97fff19SSepherosa Ziehau 		 */
3661a97fff19SSepherosa Ziehau 		ETHER_BPF_MTAP(hn_ifp, m_new);
3662a97fff19SSepherosa Ziehau 
3663a97fff19SSepherosa Ziehau 		/*
3664a97fff19SSepherosa Ziehau 		 * Update hn(4)'s stats.
3665a97fff19SSepherosa Ziehau 		 */
3666a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
3667a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IBYTES, m_new->m_pkthdr.len);
3668a97fff19SSepherosa Ziehau 		/* Checked at the beginning of this function. */
3669a97fff19SSepherosa Ziehau 		KASSERT(m_new->m_len >= ETHER_HDR_LEN, ("not ethernet frame"));
3670a97fff19SSepherosa Ziehau 		eh = mtod(m_new, struct ether_header *);
3671a97fff19SSepherosa Ziehau 		if (ETHER_IS_MULTICAST(eh->ether_dhost))
3672a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IMCASTS, 1);
3673a97fff19SSepherosa Ziehau 	}
367415516c77SSepherosa Ziehau 	rxr->hn_pkts++;
367515516c77SSepherosa Ziehau 
3676a97fff19SSepherosa Ziehau 	if ((hn_ifp->if_capenable & IFCAP_LRO) && do_lro) {
367715516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
367815516c77SSepherosa Ziehau 		struct lro_ctrl *lro = &rxr->hn_lro;
367915516c77SSepherosa Ziehau 
368015516c77SSepherosa Ziehau 		if (lro->lro_cnt) {
368115516c77SSepherosa Ziehau 			rxr->hn_lro_tried++;
368215516c77SSepherosa Ziehau 			if (hn_lro_rx(lro, m_new) == 0) {
368315516c77SSepherosa Ziehau 				/* DONE! */
368415516c77SSepherosa Ziehau 				return 0;
368515516c77SSepherosa Ziehau 			}
368615516c77SSepherosa Ziehau 		}
368715516c77SSepherosa Ziehau #endif
368815516c77SSepherosa Ziehau 	}
3689a97fff19SSepherosa Ziehau 	ifp->if_input(ifp, m_new);
369015516c77SSepherosa Ziehau 
369115516c77SSepherosa Ziehau 	return (0);
369215516c77SSepherosa Ziehau }
369315516c77SSepherosa Ziehau 
369415516c77SSepherosa Ziehau static int
369515516c77SSepherosa Ziehau hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
369615516c77SSepherosa Ziehau {
369715516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
36989c6cae24SSepherosa Ziehau 	struct ifreq *ifr = (struct ifreq *)data, ifr_vf;
36999c6cae24SSepherosa Ziehau 	struct ifnet *vf_ifp;
370015516c77SSepherosa Ziehau 	int mask, error = 0;
37018c068aa5SSepherosa Ziehau 	struct ifrsskey *ifrk;
37028c068aa5SSepherosa Ziehau 	struct ifrsshash *ifrh;
3703eb2fe044SSepherosa Ziehau 	uint32_t mtu;
370415516c77SSepherosa Ziehau 
370515516c77SSepherosa Ziehau 	switch (cmd) {
370615516c77SSepherosa Ziehau 	case SIOCSIFMTU:
370715516c77SSepherosa Ziehau 		if (ifr->ifr_mtu > HN_MTU_MAX) {
370815516c77SSepherosa Ziehau 			error = EINVAL;
370915516c77SSepherosa Ziehau 			break;
371015516c77SSepherosa Ziehau 		}
371115516c77SSepherosa Ziehau 
371215516c77SSepherosa Ziehau 		HN_LOCK(sc);
371315516c77SSepherosa Ziehau 
371415516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
371515516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
371615516c77SSepherosa Ziehau 			break;
371715516c77SSepherosa Ziehau 		}
371815516c77SSepherosa Ziehau 
371915516c77SSepherosa Ziehau 		if ((sc->hn_caps & HN_CAP_MTU) == 0) {
372015516c77SSepherosa Ziehau 			/* Can't change MTU */
372115516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
372215516c77SSepherosa Ziehau 			error = EOPNOTSUPP;
372315516c77SSepherosa Ziehau 			break;
372415516c77SSepherosa Ziehau 		}
372515516c77SSepherosa Ziehau 
372615516c77SSepherosa Ziehau 		if (ifp->if_mtu == ifr->ifr_mtu) {
372715516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
372815516c77SSepherosa Ziehau 			break;
372915516c77SSepherosa Ziehau 		}
373015516c77SSepherosa Ziehau 
37319c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
37329c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
37339c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
37349c6cae24SSepherosa Ziehau 			strlcpy(ifr_vf.ifr_name, vf_ifp->if_xname,
37359c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
37369c6cae24SSepherosa Ziehau 			error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFMTU,
37379c6cae24SSepherosa Ziehau 			    (caddr_t)&ifr_vf);
37389c6cae24SSepherosa Ziehau 			if (error) {
37399c6cae24SSepherosa Ziehau 				HN_UNLOCK(sc);
37409c6cae24SSepherosa Ziehau 				if_printf(ifp, "%s SIOCSIFMTU %d failed: %d\n",
37419c6cae24SSepherosa Ziehau 				    vf_ifp->if_xname, ifr->ifr_mtu, error);
37429c6cae24SSepherosa Ziehau 				break;
37439c6cae24SSepherosa Ziehau 			}
37449c6cae24SSepherosa Ziehau 		}
37459c6cae24SSepherosa Ziehau 
374615516c77SSepherosa Ziehau 		/*
374715516c77SSepherosa Ziehau 		 * Suspend this interface before the synthetic parts
374815516c77SSepherosa Ziehau 		 * are ripped.
374915516c77SSepherosa Ziehau 		 */
375015516c77SSepherosa Ziehau 		hn_suspend(sc);
375115516c77SSepherosa Ziehau 
375215516c77SSepherosa Ziehau 		/*
375315516c77SSepherosa Ziehau 		 * Detach the synthetics parts, i.e. NVS and RNDIS.
375415516c77SSepherosa Ziehau 		 */
375515516c77SSepherosa Ziehau 		hn_synth_detach(sc);
375615516c77SSepherosa Ziehau 
375715516c77SSepherosa Ziehau 		/*
375815516c77SSepherosa Ziehau 		 * Reattach the synthetic parts, i.e. NVS and RNDIS,
375915516c77SSepherosa Ziehau 		 * with the new MTU setting.
376015516c77SSepherosa Ziehau 		 */
376115516c77SSepherosa Ziehau 		error = hn_synth_attach(sc, ifr->ifr_mtu);
376215516c77SSepherosa Ziehau 		if (error) {
376315516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
376415516c77SSepherosa Ziehau 			break;
376515516c77SSepherosa Ziehau 		}
376615516c77SSepherosa Ziehau 
3767eb2fe044SSepherosa Ziehau 		error = hn_rndis_get_mtu(sc, &mtu);
3768eb2fe044SSepherosa Ziehau 		if (error)
3769eb2fe044SSepherosa Ziehau 			mtu = ifr->ifr_mtu;
3770eb2fe044SSepherosa Ziehau 		else if (bootverbose)
3771eb2fe044SSepherosa Ziehau 			if_printf(ifp, "RNDIS mtu %u\n", mtu);
3772eb2fe044SSepherosa Ziehau 
377315516c77SSepherosa Ziehau 		/*
377415516c77SSepherosa Ziehau 		 * Commit the requested MTU, after the synthetic parts
377515516c77SSepherosa Ziehau 		 * have been successfully attached.
377615516c77SSepherosa Ziehau 		 */
3777eb2fe044SSepherosa Ziehau 		if (mtu >= ifr->ifr_mtu) {
3778eb2fe044SSepherosa Ziehau 			mtu = ifr->ifr_mtu;
3779eb2fe044SSepherosa Ziehau 		} else {
3780eb2fe044SSepherosa Ziehau 			if_printf(ifp, "fixup mtu %d -> %u\n",
3781eb2fe044SSepherosa Ziehau 			    ifr->ifr_mtu, mtu);
3782eb2fe044SSepherosa Ziehau 		}
3783eb2fe044SSepherosa Ziehau 		ifp->if_mtu = mtu;
378415516c77SSepherosa Ziehau 
378515516c77SSepherosa Ziehau 		/*
37869c6cae24SSepherosa Ziehau 		 * Synthetic parts' reattach may change the chimney
37879c6cae24SSepherosa Ziehau 		 * sending size; update it.
378815516c77SSepherosa Ziehau 		 */
378915516c77SSepherosa Ziehau 		if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
379015516c77SSepherosa Ziehau 			hn_set_chim_size(sc, sc->hn_chim_szmax);
37919c6cae24SSepherosa Ziehau 
37929c6cae24SSepherosa Ziehau 		/*
37939c6cae24SSepherosa Ziehau 		 * Make sure that various parameters based on MTU are
37949c6cae24SSepherosa Ziehau 		 * still valid, after the MTU change.
37959c6cae24SSepherosa Ziehau 		 */
37969c6cae24SSepherosa Ziehau 		hn_mtu_change_fixup(sc);
379715516c77SSepherosa Ziehau 
379815516c77SSepherosa Ziehau 		/*
379915516c77SSepherosa Ziehau 		 * All done!  Resume the interface now.
380015516c77SSepherosa Ziehau 		 */
380115516c77SSepherosa Ziehau 		hn_resume(sc);
380215516c77SSepherosa Ziehau 
3803d0cd8231SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXVF) ||
3804d0cd8231SSepherosa Ziehau 		    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
38059c6cae24SSepherosa Ziehau 			/*
38069c6cae24SSepherosa Ziehau 			 * Since we have reattached the NVS part,
38079c6cae24SSepherosa Ziehau 			 * change the datapath to VF again; in case
38089c6cae24SSepherosa Ziehau 			 * that it is lost, after the NVS was detached.
38099c6cae24SSepherosa Ziehau 			 */
38109c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
38119c6cae24SSepherosa Ziehau 		}
38129c6cae24SSepherosa Ziehau 
381315516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
381415516c77SSepherosa Ziehau 		break;
381515516c77SSepherosa Ziehau 
381615516c77SSepherosa Ziehau 	case SIOCSIFFLAGS:
381715516c77SSepherosa Ziehau 		HN_LOCK(sc);
381815516c77SSepherosa Ziehau 
381915516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
382015516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
382115516c77SSepherosa Ziehau 			break;
382215516c77SSepherosa Ziehau 		}
382315516c77SSepherosa Ziehau 
38249c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc))
38259c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
38269c6cae24SSepherosa Ziehau 
382715516c77SSepherosa Ziehau 		if (ifp->if_flags & IFF_UP) {
3828fdc4f478SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3829fdc4f478SSepherosa Ziehau 				/*
3830fdc4f478SSepherosa Ziehau 				 * Caller meight hold mutex, e.g.
3831fdc4f478SSepherosa Ziehau 				 * bpf; use busy-wait for the RNDIS
3832fdc4f478SSepherosa Ziehau 				 * reply.
3833fdc4f478SSepherosa Ziehau 				 */
3834fdc4f478SSepherosa Ziehau 				HN_NO_SLEEPING(sc);
3835c08f7b2cSSepherosa Ziehau 				hn_rxfilter_config(sc);
3836fdc4f478SSepherosa Ziehau 				HN_SLEEPING_OK(sc);
38379c6cae24SSepherosa Ziehau 
38389c6cae24SSepherosa Ziehau 				if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
38399c6cae24SSepherosa Ziehau 					error = hn_xpnt_vf_iocsetflags(sc);
3840fdc4f478SSepherosa Ziehau 			} else {
384115516c77SSepherosa Ziehau 				hn_init_locked(sc);
3842fdc4f478SSepherosa Ziehau 			}
384315516c77SSepherosa Ziehau 		} else {
384415516c77SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
38455bdfd3fdSDexuan Cui 				hn_stop(sc, false);
384615516c77SSepherosa Ziehau 		}
384715516c77SSepherosa Ziehau 		sc->hn_if_flags = ifp->if_flags;
384815516c77SSepherosa Ziehau 
384915516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
385015516c77SSepherosa Ziehau 		break;
385115516c77SSepherosa Ziehau 
385215516c77SSepherosa Ziehau 	case SIOCSIFCAP:
385315516c77SSepherosa Ziehau 		HN_LOCK(sc);
38549c6cae24SSepherosa Ziehau 
38559c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
38569c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
38579c6cae24SSepherosa Ziehau 			strlcpy(ifr_vf.ifr_name, sc->hn_vf_ifp->if_xname,
38589c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
38599c6cae24SSepherosa Ziehau 			error = hn_xpnt_vf_iocsetcaps(sc, &ifr_vf);
38609c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
38619c6cae24SSepherosa Ziehau 			break;
38629c6cae24SSepherosa Ziehau 		}
38639c6cae24SSepherosa Ziehau 
38649c6cae24SSepherosa Ziehau 		/*
38659c6cae24SSepherosa Ziehau 		 * Fix up requested capabilities w/ supported capabilities,
38669c6cae24SSepherosa Ziehau 		 * since the supported capabilities could have been changed.
38679c6cae24SSepherosa Ziehau 		 */
38689c6cae24SSepherosa Ziehau 		mask = (ifr->ifr_reqcap & ifp->if_capabilities) ^
38699c6cae24SSepherosa Ziehau 		    ifp->if_capenable;
387015516c77SSepherosa Ziehau 
387115516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM) {
387215516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
387315516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
387415516c77SSepherosa Ziehau 				ifp->if_hwassist |= HN_CSUM_IP_HWASSIST(sc);
387515516c77SSepherosa Ziehau 			else
387615516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~HN_CSUM_IP_HWASSIST(sc);
387715516c77SSepherosa Ziehau 		}
387815516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM_IPV6) {
387915516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM_IPV6;
388015516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
388115516c77SSepherosa Ziehau 				ifp->if_hwassist |= HN_CSUM_IP6_HWASSIST(sc);
388215516c77SSepherosa Ziehau 			else
388315516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~HN_CSUM_IP6_HWASSIST(sc);
388415516c77SSepherosa Ziehau 		}
388515516c77SSepherosa Ziehau 
388615516c77SSepherosa Ziehau 		/* TODO: flip RNDIS offload parameters for RXCSUM. */
388715516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
388815516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
388915516c77SSepherosa Ziehau #ifdef foo
389015516c77SSepherosa Ziehau 		/* We can't diff IPv6 packets from IPv4 packets on RX path. */
389115516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM_IPV6)
389215516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM_IPV6;
389315516c77SSepherosa Ziehau #endif
389415516c77SSepherosa Ziehau 
389515516c77SSepherosa Ziehau 		if (mask & IFCAP_LRO)
389615516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_LRO;
389715516c77SSepherosa Ziehau 
389815516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO4) {
389915516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO4;
390015516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO4)
390115516c77SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_IP_TSO;
390215516c77SSepherosa Ziehau 			else
390315516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_IP_TSO;
390415516c77SSepherosa Ziehau 		}
390515516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO6) {
390615516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO6;
390715516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO6)
390815516c77SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_IP6_TSO;
390915516c77SSepherosa Ziehau 			else
391015516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_IP6_TSO;
391115516c77SSepherosa Ziehau 		}
391215516c77SSepherosa Ziehau 
391315516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
391415516c77SSepherosa Ziehau 		break;
391515516c77SSepherosa Ziehau 
391615516c77SSepherosa Ziehau 	case SIOCADDMULTI:
391715516c77SSepherosa Ziehau 	case SIOCDELMULTI:
391815516c77SSepherosa Ziehau 		HN_LOCK(sc);
391915516c77SSepherosa Ziehau 
392015516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
392115516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
392215516c77SSepherosa Ziehau 			break;
392315516c77SSepherosa Ziehau 		}
3924fdc4f478SSepherosa Ziehau 		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3925fdc4f478SSepherosa Ziehau 			/*
3926fdc4f478SSepherosa Ziehau 			 * Multicast uses mutex; use busy-wait for
3927fdc4f478SSepherosa Ziehau 			 * the RNDIS reply.
3928fdc4f478SSepherosa Ziehau 			 */
3929fdc4f478SSepherosa Ziehau 			HN_NO_SLEEPING(sc);
3930c08f7b2cSSepherosa Ziehau 			hn_rxfilter_config(sc);
3931fdc4f478SSepherosa Ziehau 			HN_SLEEPING_OK(sc);
3932fdc4f478SSepherosa Ziehau 		}
393315516c77SSepherosa Ziehau 
39349c6cae24SSepherosa Ziehau 		/* XXX vlan(4) style mcast addr maintenance */
39359c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
39369c6cae24SSepherosa Ziehau 			int old_if_flags;
39379c6cae24SSepherosa Ziehau 
39389c6cae24SSepherosa Ziehau 			old_if_flags = sc->hn_vf_ifp->if_flags;
39399c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
39409c6cae24SSepherosa Ziehau 
39419c6cae24SSepherosa Ziehau 			if ((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) &&
39429c6cae24SSepherosa Ziehau 			    ((old_if_flags ^ sc->hn_vf_ifp->if_flags) &
39439c6cae24SSepherosa Ziehau 			     IFF_ALLMULTI))
39449c6cae24SSepherosa Ziehau 				error = hn_xpnt_vf_iocsetflags(sc);
39459c6cae24SSepherosa Ziehau 		}
39469c6cae24SSepherosa Ziehau 
394715516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
394815516c77SSepherosa Ziehau 		break;
394915516c77SSepherosa Ziehau 
395015516c77SSepherosa Ziehau 	case SIOCSIFMEDIA:
395115516c77SSepherosa Ziehau 	case SIOCGIFMEDIA:
39529c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
39539c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
39549c6cae24SSepherosa Ziehau 			/*
39559c6cae24SSepherosa Ziehau 			 * SIOCGIFMEDIA expects ifmediareq, so don't
39569c6cae24SSepherosa Ziehau 			 * create and pass ifr_vf to the VF here; just
39579c6cae24SSepherosa Ziehau 			 * replace the ifr_name.
39589c6cae24SSepherosa Ziehau 			 */
39599c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
39609c6cae24SSepherosa Ziehau 			strlcpy(ifr->ifr_name, vf_ifp->if_xname,
39619c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
39629c6cae24SSepherosa Ziehau 			error = vf_ifp->if_ioctl(vf_ifp, cmd, data);
39639c6cae24SSepherosa Ziehau 			/* Restore the ifr_name. */
39649c6cae24SSepherosa Ziehau 			strlcpy(ifr->ifr_name, ifp->if_xname,
39659c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
39669c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
39679c6cae24SSepherosa Ziehau 			break;
39689c6cae24SSepherosa Ziehau 		}
39699c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
397015516c77SSepherosa Ziehau 		error = ifmedia_ioctl(ifp, ifr, &sc->hn_media, cmd);
397115516c77SSepherosa Ziehau 		break;
397215516c77SSepherosa Ziehau 
39738c068aa5SSepherosa Ziehau 	case SIOCGIFRSSHASH:
39748c068aa5SSepherosa Ziehau 		ifrh = (struct ifrsshash *)data;
39758c068aa5SSepherosa Ziehau 		HN_LOCK(sc);
39768c068aa5SSepherosa Ziehau 		if (sc->hn_rx_ring_inuse == 1) {
39778c068aa5SSepherosa Ziehau 			HN_UNLOCK(sc);
39788c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_NONE;
39798c068aa5SSepherosa Ziehau 			ifrh->ifrh_types = 0;
39808c068aa5SSepherosa Ziehau 			break;
39818c068aa5SSepherosa Ziehau 		}
39828c068aa5SSepherosa Ziehau 
39838c068aa5SSepherosa Ziehau 		if (sc->hn_rss_hash & NDIS_HASH_FUNCTION_TOEPLITZ)
39848c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_TOEPLITZ;
39858c068aa5SSepherosa Ziehau 		else
39868c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_PRIVATE;
3987642ec226SSepherosa Ziehau 		ifrh->ifrh_types = hn_rss_type_fromndis(sc->hn_rss_hash);
39888c068aa5SSepherosa Ziehau 		HN_UNLOCK(sc);
39898c068aa5SSepherosa Ziehau 		break;
39908c068aa5SSepherosa Ziehau 
39918c068aa5SSepherosa Ziehau 	case SIOCGIFRSSKEY:
39928c068aa5SSepherosa Ziehau 		ifrk = (struct ifrsskey *)data;
39938c068aa5SSepherosa Ziehau 		HN_LOCK(sc);
39948c068aa5SSepherosa Ziehau 		if (sc->hn_rx_ring_inuse == 1) {
39958c068aa5SSepherosa Ziehau 			HN_UNLOCK(sc);
39968c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_NONE;
39978c068aa5SSepherosa Ziehau 			ifrk->ifrk_keylen = 0;
39988c068aa5SSepherosa Ziehau 			break;
39998c068aa5SSepherosa Ziehau 		}
40008c068aa5SSepherosa Ziehau 		if (sc->hn_rss_hash & NDIS_HASH_FUNCTION_TOEPLITZ)
40018c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_TOEPLITZ;
40028c068aa5SSepherosa Ziehau 		else
40038c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_PRIVATE;
40048c068aa5SSepherosa Ziehau 		ifrk->ifrk_keylen = NDIS_HASH_KEYSIZE_TOEPLITZ;
40058c068aa5SSepherosa Ziehau 		memcpy(ifrk->ifrk_key, sc->hn_rss.rss_key,
40068c068aa5SSepherosa Ziehau 		    NDIS_HASH_KEYSIZE_TOEPLITZ);
40078c068aa5SSepherosa Ziehau 		HN_UNLOCK(sc);
40088c068aa5SSepherosa Ziehau 		break;
40098c068aa5SSepherosa Ziehau 
401015516c77SSepherosa Ziehau 	default:
401115516c77SSepherosa Ziehau 		error = ether_ioctl(ifp, cmd, data);
401215516c77SSepherosa Ziehau 		break;
401315516c77SSepherosa Ziehau 	}
401415516c77SSepherosa Ziehau 	return (error);
401515516c77SSepherosa Ziehau }
401615516c77SSepherosa Ziehau 
401715516c77SSepherosa Ziehau static void
40185bdfd3fdSDexuan Cui hn_stop(struct hn_softc *sc, bool detaching)
401915516c77SSepherosa Ziehau {
402015516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
402115516c77SSepherosa Ziehau 	int i;
402215516c77SSepherosa Ziehau 
402315516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
402415516c77SSepherosa Ziehau 
402515516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
402615516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
402715516c77SSepherosa Ziehau 
40289c6cae24SSepherosa Ziehau 	/* Clear RUNNING bit ASAP. */
40299c6cae24SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
40309c6cae24SSepherosa Ziehau 
40316c1204dfSSepherosa Ziehau 	/* Disable polling. */
40326c1204dfSSepherosa Ziehau 	hn_polling(sc, 0);
40336c1204dfSSepherosa Ziehau 
40349c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
40359c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_ifp != NULL,
40369c6cae24SSepherosa Ziehau 		    ("%s: VF is not attached", ifp->if_xname));
40379c6cae24SSepherosa Ziehau 
4038a97fff19SSepherosa Ziehau 		/* Mark transparent mode VF as disabled. */
4039a97fff19SSepherosa Ziehau 		hn_xpnt_vf_setdisable(sc, false /* keep hn_vf_ifp */);
40409c6cae24SSepherosa Ziehau 
40419c6cae24SSepherosa Ziehau 		/*
40429c6cae24SSepherosa Ziehau 		 * NOTE:
40439c6cae24SSepherosa Ziehau 		 * Datapath setting must happen _before_ bringing
40449c6cae24SSepherosa Ziehau 		 * the VF down.
40459c6cae24SSepherosa Ziehau 		 */
40469c6cae24SSepherosa Ziehau 		hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
40479c6cae24SSepherosa Ziehau 
40489c6cae24SSepherosa Ziehau 		/*
40499c6cae24SSepherosa Ziehau 		 * Bring the VF down.
40509c6cae24SSepherosa Ziehau 		 */
40519c6cae24SSepherosa Ziehau 		hn_xpnt_vf_saveifflags(sc);
40529c6cae24SSepherosa Ziehau 		sc->hn_vf_ifp->if_flags &= ~IFF_UP;
40539c6cae24SSepherosa Ziehau 		hn_xpnt_vf_iocsetflags(sc);
40549c6cae24SSepherosa Ziehau 	}
40559c6cae24SSepherosa Ziehau 
40569c6cae24SSepherosa Ziehau 	/* Suspend data transfers. */
405715516c77SSepherosa Ziehau 	hn_suspend_data(sc);
405815516c77SSepherosa Ziehau 
405915516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
406015516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
406115516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
406215516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
40635bdfd3fdSDexuan Cui 
40645bdfd3fdSDexuan Cui 	/*
40659c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is active, make sure
40669c6cae24SSepherosa Ziehau 	 * that the RX filter still allows packet reception.
40675bdfd3fdSDexuan Cui 	 */
4068962f0357SSepherosa Ziehau 	if (!detaching && (sc->hn_flags & HN_FLAG_RXVF))
40695bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
407015516c77SSepherosa Ziehau }
407115516c77SSepherosa Ziehau 
407215516c77SSepherosa Ziehau static void
407315516c77SSepherosa Ziehau hn_init_locked(struct hn_softc *sc)
407415516c77SSepherosa Ziehau {
407515516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
407615516c77SSepherosa Ziehau 	int i;
407715516c77SSepherosa Ziehau 
407815516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
407915516c77SSepherosa Ziehau 
408015516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
408115516c77SSepherosa Ziehau 		return;
408215516c77SSepherosa Ziehau 
408315516c77SSepherosa Ziehau 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
408415516c77SSepherosa Ziehau 		return;
408515516c77SSepherosa Ziehau 
408615516c77SSepherosa Ziehau 	/* Configure RX filter */
4087c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
408815516c77SSepherosa Ziehau 
408915516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
409015516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
409115516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
409215516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
409315516c77SSepherosa Ziehau 
409415516c77SSepherosa Ziehau 	/* Clear TX 'suspended' bit. */
409515516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_inuse);
409615516c77SSepherosa Ziehau 
40979c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
40989c6cae24SSepherosa Ziehau 		/* Initialize transparent VF. */
40999c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
41009c6cae24SSepherosa Ziehau 	}
41019c6cae24SSepherosa Ziehau 
410215516c77SSepherosa Ziehau 	/* Everything is ready; unleash! */
410315516c77SSepherosa Ziehau 	atomic_set_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
41046c1204dfSSepherosa Ziehau 
41056c1204dfSSepherosa Ziehau 	/* Re-enable polling if requested. */
41066c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz > 0)
41076c1204dfSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
410815516c77SSepherosa Ziehau }
410915516c77SSepherosa Ziehau 
411015516c77SSepherosa Ziehau static void
411115516c77SSepherosa Ziehau hn_init(void *xsc)
411215516c77SSepherosa Ziehau {
411315516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
411415516c77SSepherosa Ziehau 
411515516c77SSepherosa Ziehau 	HN_LOCK(sc);
411615516c77SSepherosa Ziehau 	hn_init_locked(sc);
411715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
411815516c77SSepherosa Ziehau }
411915516c77SSepherosa Ziehau 
412015516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
412115516c77SSepherosa Ziehau 
412215516c77SSepherosa Ziehau static int
412315516c77SSepherosa Ziehau hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS)
412415516c77SSepherosa Ziehau {
412515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
412615516c77SSepherosa Ziehau 	unsigned int lenlim;
412715516c77SSepherosa Ziehau 	int error;
412815516c77SSepherosa Ziehau 
412915516c77SSepherosa Ziehau 	lenlim = sc->hn_rx_ring[0].hn_lro.lro_length_lim;
413015516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &lenlim, 0, req);
413115516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
413215516c77SSepherosa Ziehau 		return error;
413315516c77SSepherosa Ziehau 
413415516c77SSepherosa Ziehau 	HN_LOCK(sc);
413515516c77SSepherosa Ziehau 	if (lenlim < HN_LRO_LENLIM_MIN(sc->hn_ifp) ||
413615516c77SSepherosa Ziehau 	    lenlim > TCP_LRO_LENGTH_MAX) {
413715516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
413815516c77SSepherosa Ziehau 		return EINVAL;
413915516c77SSepherosa Ziehau 	}
414015516c77SSepherosa Ziehau 	hn_set_lro_lenlim(sc, lenlim);
414115516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
414215516c77SSepherosa Ziehau 
414315516c77SSepherosa Ziehau 	return 0;
414415516c77SSepherosa Ziehau }
414515516c77SSepherosa Ziehau 
414615516c77SSepherosa Ziehau static int
414715516c77SSepherosa Ziehau hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS)
414815516c77SSepherosa Ziehau {
414915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
415015516c77SSepherosa Ziehau 	int ackcnt, error, i;
415115516c77SSepherosa Ziehau 
415215516c77SSepherosa Ziehau 	/*
415315516c77SSepherosa Ziehau 	 * lro_ackcnt_lim is append count limit,
415415516c77SSepherosa Ziehau 	 * +1 to turn it into aggregation limit.
415515516c77SSepherosa Ziehau 	 */
415615516c77SSepherosa Ziehau 	ackcnt = sc->hn_rx_ring[0].hn_lro.lro_ackcnt_lim + 1;
415715516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &ackcnt, 0, req);
415815516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
415915516c77SSepherosa Ziehau 		return error;
416015516c77SSepherosa Ziehau 
416115516c77SSepherosa Ziehau 	if (ackcnt < 2 || ackcnt > (TCP_LRO_ACKCNT_MAX + 1))
416215516c77SSepherosa Ziehau 		return EINVAL;
416315516c77SSepherosa Ziehau 
416415516c77SSepherosa Ziehau 	/*
416515516c77SSepherosa Ziehau 	 * Convert aggregation limit back to append
416615516c77SSepherosa Ziehau 	 * count limit.
416715516c77SSepherosa Ziehau 	 */
416815516c77SSepherosa Ziehau 	--ackcnt;
416915516c77SSepherosa Ziehau 	HN_LOCK(sc);
4170a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
417115516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_ackcnt_lim = ackcnt;
417215516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
417315516c77SSepherosa Ziehau 	return 0;
417415516c77SSepherosa Ziehau }
417515516c77SSepherosa Ziehau 
417615516c77SSepherosa Ziehau #endif
417715516c77SSepherosa Ziehau 
417815516c77SSepherosa Ziehau static int
417915516c77SSepherosa Ziehau hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS)
418015516c77SSepherosa Ziehau {
418115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
418215516c77SSepherosa Ziehau 	int hcsum = arg2;
418315516c77SSepherosa Ziehau 	int on, error, i;
418415516c77SSepherosa Ziehau 
418515516c77SSepherosa Ziehau 	on = 0;
418615516c77SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_trust_hcsum & hcsum)
418715516c77SSepherosa Ziehau 		on = 1;
418815516c77SSepherosa Ziehau 
418915516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &on, 0, req);
419015516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
419115516c77SSepherosa Ziehau 		return error;
419215516c77SSepherosa Ziehau 
419315516c77SSepherosa Ziehau 	HN_LOCK(sc);
4194a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
419515516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
419615516c77SSepherosa Ziehau 
419715516c77SSepherosa Ziehau 		if (on)
419815516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= hcsum;
419915516c77SSepherosa Ziehau 		else
420015516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum &= ~hcsum;
420115516c77SSepherosa Ziehau 	}
420215516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
420315516c77SSepherosa Ziehau 	return 0;
420415516c77SSepherosa Ziehau }
420515516c77SSepherosa Ziehau 
420615516c77SSepherosa Ziehau static int
420715516c77SSepherosa Ziehau hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS)
420815516c77SSepherosa Ziehau {
420915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
421015516c77SSepherosa Ziehau 	int chim_size, error;
421115516c77SSepherosa Ziehau 
421215516c77SSepherosa Ziehau 	chim_size = sc->hn_tx_ring[0].hn_chim_size;
421315516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &chim_size, 0, req);
421415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
421515516c77SSepherosa Ziehau 		return error;
421615516c77SSepherosa Ziehau 
421715516c77SSepherosa Ziehau 	if (chim_size > sc->hn_chim_szmax || chim_size <= 0)
421815516c77SSepherosa Ziehau 		return EINVAL;
421915516c77SSepherosa Ziehau 
422015516c77SSepherosa Ziehau 	HN_LOCK(sc);
422115516c77SSepherosa Ziehau 	hn_set_chim_size(sc, chim_size);
422215516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
422315516c77SSepherosa Ziehau 	return 0;
422415516c77SSepherosa Ziehau }
422515516c77SSepherosa Ziehau 
422615516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
422715516c77SSepherosa Ziehau static int
422815516c77SSepherosa Ziehau hn_rx_stat_int_sysctl(SYSCTL_HANDLER_ARGS)
422915516c77SSepherosa Ziehau {
423015516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
423115516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
423215516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
423315516c77SSepherosa Ziehau 	uint64_t stat;
423415516c77SSepherosa Ziehau 
423515516c77SSepherosa Ziehau 	stat = 0;
423615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
423715516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
423815516c77SSepherosa Ziehau 		stat += *((int *)((uint8_t *)rxr + ofs));
423915516c77SSepherosa Ziehau 	}
424015516c77SSepherosa Ziehau 
424115516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
424215516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
424315516c77SSepherosa Ziehau 		return error;
424415516c77SSepherosa Ziehau 
424515516c77SSepherosa Ziehau 	/* Zero out this stat. */
424615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
424715516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
424815516c77SSepherosa Ziehau 		*((int *)((uint8_t *)rxr + ofs)) = 0;
424915516c77SSepherosa Ziehau 	}
425015516c77SSepherosa Ziehau 	return 0;
425115516c77SSepherosa Ziehau }
425215516c77SSepherosa Ziehau #else
425315516c77SSepherosa Ziehau static int
425415516c77SSepherosa Ziehau hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS)
425515516c77SSepherosa Ziehau {
425615516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
425715516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
425815516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
425915516c77SSepherosa Ziehau 	uint64_t stat;
426015516c77SSepherosa Ziehau 
426115516c77SSepherosa Ziehau 	stat = 0;
4262a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
426315516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
426415516c77SSepherosa Ziehau 		stat += *((uint64_t *)((uint8_t *)rxr + ofs));
426515516c77SSepherosa Ziehau 	}
426615516c77SSepherosa Ziehau 
426715516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
426815516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
426915516c77SSepherosa Ziehau 		return error;
427015516c77SSepherosa Ziehau 
427115516c77SSepherosa Ziehau 	/* Zero out this stat. */
4272a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
427315516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
427415516c77SSepherosa Ziehau 		*((uint64_t *)((uint8_t *)rxr + ofs)) = 0;
427515516c77SSepherosa Ziehau 	}
427615516c77SSepherosa Ziehau 	return 0;
427715516c77SSepherosa Ziehau }
427815516c77SSepherosa Ziehau 
427915516c77SSepherosa Ziehau #endif
428015516c77SSepherosa Ziehau 
428115516c77SSepherosa Ziehau static int
428215516c77SSepherosa Ziehau hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
428315516c77SSepherosa Ziehau {
428415516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
428515516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
428615516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
428715516c77SSepherosa Ziehau 	u_long stat;
428815516c77SSepherosa Ziehau 
428915516c77SSepherosa Ziehau 	stat = 0;
4290a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
429115516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
429215516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)rxr + ofs));
429315516c77SSepherosa Ziehau 	}
429415516c77SSepherosa Ziehau 
429515516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
429615516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
429715516c77SSepherosa Ziehau 		return error;
429815516c77SSepherosa Ziehau 
429915516c77SSepherosa Ziehau 	/* Zero out this stat. */
4300a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
430115516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
430215516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)rxr + ofs)) = 0;
430315516c77SSepherosa Ziehau 	}
430415516c77SSepherosa Ziehau 	return 0;
430515516c77SSepherosa Ziehau }
430615516c77SSepherosa Ziehau 
430715516c77SSepherosa Ziehau static int
430815516c77SSepherosa Ziehau hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
430915516c77SSepherosa Ziehau {
431015516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
431115516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
431215516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
431315516c77SSepherosa Ziehau 	u_long stat;
431415516c77SSepherosa Ziehau 
431515516c77SSepherosa Ziehau 	stat = 0;
4316a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
431715516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
431815516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)txr + ofs));
431915516c77SSepherosa Ziehau 	}
432015516c77SSepherosa Ziehau 
432115516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
432215516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
432315516c77SSepherosa Ziehau 		return error;
432415516c77SSepherosa Ziehau 
432515516c77SSepherosa Ziehau 	/* Zero out this stat. */
4326a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
432715516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
432815516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)txr + ofs)) = 0;
432915516c77SSepherosa Ziehau 	}
433015516c77SSepherosa Ziehau 	return 0;
433115516c77SSepherosa Ziehau }
433215516c77SSepherosa Ziehau 
433315516c77SSepherosa Ziehau static int
433415516c77SSepherosa Ziehau hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS)
433515516c77SSepherosa Ziehau {
433615516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
433715516c77SSepherosa Ziehau 	int ofs = arg2, i, error, conf;
433815516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
433915516c77SSepherosa Ziehau 
434015516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[0];
434115516c77SSepherosa Ziehau 	conf = *((int *)((uint8_t *)txr + ofs));
434215516c77SSepherosa Ziehau 
434315516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &conf, 0, req);
434415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
434515516c77SSepherosa Ziehau 		return error;
434615516c77SSepherosa Ziehau 
434715516c77SSepherosa Ziehau 	HN_LOCK(sc);
4348a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
434915516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
435015516c77SSepherosa Ziehau 		*((int *)((uint8_t *)txr + ofs)) = conf;
435115516c77SSepherosa Ziehau 	}
435215516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
435315516c77SSepherosa Ziehau 
435415516c77SSepherosa Ziehau 	return 0;
435515516c77SSepherosa Ziehau }
435615516c77SSepherosa Ziehau 
435715516c77SSepherosa Ziehau static int
4358dc13fee6SSepherosa Ziehau hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS)
4359dc13fee6SSepherosa Ziehau {
4360dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4361dc13fee6SSepherosa Ziehau 	int error, size;
4362dc13fee6SSepherosa Ziehau 
4363dc13fee6SSepherosa Ziehau 	size = sc->hn_agg_size;
4364dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &size, 0, req);
4365dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
4366dc13fee6SSepherosa Ziehau 		return (error);
4367dc13fee6SSepherosa Ziehau 
4368dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
4369dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = size;
4370dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
4371dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
4372dc13fee6SSepherosa Ziehau 
4373dc13fee6SSepherosa Ziehau 	return (0);
4374dc13fee6SSepherosa Ziehau }
4375dc13fee6SSepherosa Ziehau 
4376dc13fee6SSepherosa Ziehau static int
4377dc13fee6SSepherosa Ziehau hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS)
4378dc13fee6SSepherosa Ziehau {
4379dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4380dc13fee6SSepherosa Ziehau 	int error, pkts;
4381dc13fee6SSepherosa Ziehau 
4382dc13fee6SSepherosa Ziehau 	pkts = sc->hn_agg_pkts;
4383dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pkts, 0, req);
4384dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
4385dc13fee6SSepherosa Ziehau 		return (error);
4386dc13fee6SSepherosa Ziehau 
4387dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
4388dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = pkts;
4389dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
4390dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
4391dc13fee6SSepherosa Ziehau 
4392dc13fee6SSepherosa Ziehau 	return (0);
4393dc13fee6SSepherosa Ziehau }
4394dc13fee6SSepherosa Ziehau 
4395dc13fee6SSepherosa Ziehau static int
4396dc13fee6SSepherosa Ziehau hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS)
4397dc13fee6SSepherosa Ziehau {
4398dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4399dc13fee6SSepherosa Ziehau 	int pkts;
4400dc13fee6SSepherosa Ziehau 
4401dc13fee6SSepherosa Ziehau 	pkts = sc->hn_tx_ring[0].hn_agg_pktmax;
4402dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &pkts, 0, req));
4403dc13fee6SSepherosa Ziehau }
4404dc13fee6SSepherosa Ziehau 
4405dc13fee6SSepherosa Ziehau static int
4406dc13fee6SSepherosa Ziehau hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS)
4407dc13fee6SSepherosa Ziehau {
4408dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4409dc13fee6SSepherosa Ziehau 	int align;
4410dc13fee6SSepherosa Ziehau 
4411dc13fee6SSepherosa Ziehau 	align = sc->hn_tx_ring[0].hn_agg_align;
4412dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &align, 0, req));
4413dc13fee6SSepherosa Ziehau }
4414dc13fee6SSepherosa Ziehau 
44156c1204dfSSepherosa Ziehau static void
44166c1204dfSSepherosa Ziehau hn_chan_polling(struct vmbus_channel *chan, u_int pollhz)
44176c1204dfSSepherosa Ziehau {
44186c1204dfSSepherosa Ziehau 	if (pollhz == 0)
44196c1204dfSSepherosa Ziehau 		vmbus_chan_poll_disable(chan);
44206c1204dfSSepherosa Ziehau 	else
44216c1204dfSSepherosa Ziehau 		vmbus_chan_poll_enable(chan, pollhz);
44226c1204dfSSepherosa Ziehau }
44236c1204dfSSepherosa Ziehau 
44246c1204dfSSepherosa Ziehau static void
44256c1204dfSSepherosa Ziehau hn_polling(struct hn_softc *sc, u_int pollhz)
44266c1204dfSSepherosa Ziehau {
44276c1204dfSSepherosa Ziehau 	int nsubch = sc->hn_rx_ring_inuse - 1;
44286c1204dfSSepherosa Ziehau 
44296c1204dfSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
44306c1204dfSSepherosa Ziehau 
44316c1204dfSSepherosa Ziehau 	if (nsubch > 0) {
44326c1204dfSSepherosa Ziehau 		struct vmbus_channel **subch;
44336c1204dfSSepherosa Ziehau 		int i;
44346c1204dfSSepherosa Ziehau 
44356c1204dfSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
44366c1204dfSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
44376c1204dfSSepherosa Ziehau 			hn_chan_polling(subch[i], pollhz);
44386c1204dfSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
44396c1204dfSSepherosa Ziehau 	}
44406c1204dfSSepherosa Ziehau 	hn_chan_polling(sc->hn_prichan, pollhz);
44416c1204dfSSepherosa Ziehau }
44426c1204dfSSepherosa Ziehau 
44436c1204dfSSepherosa Ziehau static int
44446c1204dfSSepherosa Ziehau hn_polling_sysctl(SYSCTL_HANDLER_ARGS)
44456c1204dfSSepherosa Ziehau {
44466c1204dfSSepherosa Ziehau 	struct hn_softc *sc = arg1;
44476c1204dfSSepherosa Ziehau 	int pollhz, error;
44486c1204dfSSepherosa Ziehau 
44496c1204dfSSepherosa Ziehau 	pollhz = sc->hn_pollhz;
44506c1204dfSSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pollhz, 0, req);
44516c1204dfSSepherosa Ziehau 	if (error || req->newptr == NULL)
44526c1204dfSSepherosa Ziehau 		return (error);
44536c1204dfSSepherosa Ziehau 
44546c1204dfSSepherosa Ziehau 	if (pollhz != 0 &&
44556c1204dfSSepherosa Ziehau 	    (pollhz < VMBUS_CHAN_POLLHZ_MIN || pollhz > VMBUS_CHAN_POLLHZ_MAX))
44566c1204dfSSepherosa Ziehau 		return (EINVAL);
44576c1204dfSSepherosa Ziehau 
44586c1204dfSSepherosa Ziehau 	HN_LOCK(sc);
44596c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz != pollhz) {
44606c1204dfSSepherosa Ziehau 		sc->hn_pollhz = pollhz;
44616c1204dfSSepherosa Ziehau 		if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) &&
44626c1204dfSSepherosa Ziehau 		    (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
44636c1204dfSSepherosa Ziehau 			hn_polling(sc, sc->hn_pollhz);
44646c1204dfSSepherosa Ziehau 	}
44656c1204dfSSepherosa Ziehau 	HN_UNLOCK(sc);
44666c1204dfSSepherosa Ziehau 
44676c1204dfSSepherosa Ziehau 	return (0);
44686c1204dfSSepherosa Ziehau }
44696c1204dfSSepherosa Ziehau 
4470dc13fee6SSepherosa Ziehau static int
447115516c77SSepherosa Ziehau hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS)
447215516c77SSepherosa Ziehau {
447315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
447415516c77SSepherosa Ziehau 	char verstr[16];
447515516c77SSepherosa Ziehau 
447615516c77SSepherosa Ziehau 	snprintf(verstr, sizeof(verstr), "%u.%u",
447715516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
447815516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
447915516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
448015516c77SSepherosa Ziehau }
448115516c77SSepherosa Ziehau 
448215516c77SSepherosa Ziehau static int
448315516c77SSepherosa Ziehau hn_caps_sysctl(SYSCTL_HANDLER_ARGS)
448415516c77SSepherosa Ziehau {
448515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
448615516c77SSepherosa Ziehau 	char caps_str[128];
448715516c77SSepherosa Ziehau 	uint32_t caps;
448815516c77SSepherosa Ziehau 
448915516c77SSepherosa Ziehau 	HN_LOCK(sc);
449015516c77SSepherosa Ziehau 	caps = sc->hn_caps;
449115516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
449215516c77SSepherosa Ziehau 	snprintf(caps_str, sizeof(caps_str), "%b", caps, HN_CAP_BITS);
449315516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, caps_str, sizeof(caps_str), req);
449415516c77SSepherosa Ziehau }
449515516c77SSepherosa Ziehau 
449615516c77SSepherosa Ziehau static int
449715516c77SSepherosa Ziehau hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS)
449815516c77SSepherosa Ziehau {
449915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
450015516c77SSepherosa Ziehau 	char assist_str[128];
450115516c77SSepherosa Ziehau 	uint32_t hwassist;
450215516c77SSepherosa Ziehau 
450315516c77SSepherosa Ziehau 	HN_LOCK(sc);
450415516c77SSepherosa Ziehau 	hwassist = sc->hn_ifp->if_hwassist;
450515516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
450615516c77SSepherosa Ziehau 	snprintf(assist_str, sizeof(assist_str), "%b", hwassist, CSUM_BITS);
450715516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, assist_str, sizeof(assist_str), req);
450815516c77SSepherosa Ziehau }
450915516c77SSepherosa Ziehau 
451015516c77SSepherosa Ziehau static int
451115516c77SSepherosa Ziehau hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS)
451215516c77SSepherosa Ziehau {
451315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
451415516c77SSepherosa Ziehau 	char filter_str[128];
451515516c77SSepherosa Ziehau 	uint32_t filter;
451615516c77SSepherosa Ziehau 
451715516c77SSepherosa Ziehau 	HN_LOCK(sc);
451815516c77SSepherosa Ziehau 	filter = sc->hn_rx_filter;
451915516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
452015516c77SSepherosa Ziehau 	snprintf(filter_str, sizeof(filter_str), "%b", filter,
452115516c77SSepherosa Ziehau 	    NDIS_PACKET_TYPES);
452215516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req);
452315516c77SSepherosa Ziehau }
452415516c77SSepherosa Ziehau 
452534d68912SSepherosa Ziehau #ifndef RSS
452634d68912SSepherosa Ziehau 
452715516c77SSepherosa Ziehau static int
452815516c77SSepherosa Ziehau hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS)
452915516c77SSepherosa Ziehau {
453015516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
453115516c77SSepherosa Ziehau 	int error;
453215516c77SSepherosa Ziehau 
453315516c77SSepherosa Ziehau 	HN_LOCK(sc);
453415516c77SSepherosa Ziehau 
453515516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
453615516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
453715516c77SSepherosa Ziehau 		goto back;
453815516c77SSepherosa Ziehau 
4539642ec226SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) ||
4540642ec226SSepherosa Ziehau 	    (hn_xpnt_vf && sc->hn_vf_ifp != NULL)) {
4541642ec226SSepherosa Ziehau 		/*
4542642ec226SSepherosa Ziehau 		 * RSS key is synchronized w/ VF's, don't allow users
4543642ec226SSepherosa Ziehau 		 * to change it.
4544642ec226SSepherosa Ziehau 		 */
4545642ec226SSepherosa Ziehau 		error = EBUSY;
4546642ec226SSepherosa Ziehau 		goto back;
4547642ec226SSepherosa Ziehau 	}
4548642ec226SSepherosa Ziehau 
454915516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
455015516c77SSepherosa Ziehau 	if (error)
455115516c77SSepherosa Ziehau 		goto back;
455215516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
455315516c77SSepherosa Ziehau 
455415516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
455515516c77SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
455615516c77SSepherosa Ziehau 	} else {
455715516c77SSepherosa Ziehau 		/* Not RSS capable, at least for now; just save the RSS key. */
455815516c77SSepherosa Ziehau 		error = 0;
455915516c77SSepherosa Ziehau 	}
456015516c77SSepherosa Ziehau back:
456115516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
456215516c77SSepherosa Ziehau 	return (error);
456315516c77SSepherosa Ziehau }
456415516c77SSepherosa Ziehau 
456515516c77SSepherosa Ziehau static int
456615516c77SSepherosa Ziehau hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS)
456715516c77SSepherosa Ziehau {
456815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
456915516c77SSepherosa Ziehau 	int error;
457015516c77SSepherosa Ziehau 
457115516c77SSepherosa Ziehau 	HN_LOCK(sc);
457215516c77SSepherosa Ziehau 
457315516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
457415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
457515516c77SSepherosa Ziehau 		goto back;
457615516c77SSepherosa Ziehau 
457715516c77SSepherosa Ziehau 	/*
457815516c77SSepherosa Ziehau 	 * Don't allow RSS indirect table change, if this interface is not
457915516c77SSepherosa Ziehau 	 * RSS capable currently.
458015516c77SSepherosa Ziehau 	 */
458115516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
458215516c77SSepherosa Ziehau 		error = EOPNOTSUPP;
458315516c77SSepherosa Ziehau 		goto back;
458415516c77SSepherosa Ziehau 	}
458515516c77SSepherosa Ziehau 
458615516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
458715516c77SSepherosa Ziehau 	if (error)
458815516c77SSepherosa Ziehau 		goto back;
458915516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSIND;
459015516c77SSepherosa Ziehau 
4591afd4971bSSepherosa Ziehau 	hn_rss_ind_fixup(sc);
459215516c77SSepherosa Ziehau 	error = hn_rss_reconfig(sc);
459315516c77SSepherosa Ziehau back:
459415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
459515516c77SSepherosa Ziehau 	return (error);
459615516c77SSepherosa Ziehau }
459715516c77SSepherosa Ziehau 
459834d68912SSepherosa Ziehau #endif	/* !RSS */
459934d68912SSepherosa Ziehau 
460015516c77SSepherosa Ziehau static int
460115516c77SSepherosa Ziehau hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS)
460215516c77SSepherosa Ziehau {
460315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
460415516c77SSepherosa Ziehau 	char hash_str[128];
460515516c77SSepherosa Ziehau 	uint32_t hash;
460615516c77SSepherosa Ziehau 
460715516c77SSepherosa Ziehau 	HN_LOCK(sc);
460815516c77SSepherosa Ziehau 	hash = sc->hn_rss_hash;
460915516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
461015516c77SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
461115516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
461215516c77SSepherosa Ziehau }
461315516c77SSepherosa Ziehau 
461415516c77SSepherosa Ziehau static int
4615642ec226SSepherosa Ziehau hn_rss_hcap_sysctl(SYSCTL_HANDLER_ARGS)
4616642ec226SSepherosa Ziehau {
4617642ec226SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4618642ec226SSepherosa Ziehau 	char hash_str[128];
4619642ec226SSepherosa Ziehau 	uint32_t hash;
4620642ec226SSepherosa Ziehau 
4621642ec226SSepherosa Ziehau 	HN_LOCK(sc);
4622642ec226SSepherosa Ziehau 	hash = sc->hn_rss_hcap;
4623642ec226SSepherosa Ziehau 	HN_UNLOCK(sc);
4624642ec226SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
4625642ec226SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
4626642ec226SSepherosa Ziehau }
4627642ec226SSepherosa Ziehau 
4628642ec226SSepherosa Ziehau static int
4629642ec226SSepherosa Ziehau hn_rss_mbuf_sysctl(SYSCTL_HANDLER_ARGS)
4630642ec226SSepherosa Ziehau {
4631642ec226SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4632642ec226SSepherosa Ziehau 	char hash_str[128];
4633642ec226SSepherosa Ziehau 	uint32_t hash;
4634642ec226SSepherosa Ziehau 
4635642ec226SSepherosa Ziehau 	HN_LOCK(sc);
4636642ec226SSepherosa Ziehau 	hash = sc->hn_rx_ring[0].hn_mbuf_hash;
4637642ec226SSepherosa Ziehau 	HN_UNLOCK(sc);
4638642ec226SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
4639642ec226SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
4640642ec226SSepherosa Ziehau }
4641642ec226SSepherosa Ziehau 
4642642ec226SSepherosa Ziehau static int
464340d60d6eSDexuan Cui hn_vf_sysctl(SYSCTL_HANDLER_ARGS)
464440d60d6eSDexuan Cui {
464540d60d6eSDexuan Cui 	struct hn_softc *sc = arg1;
4646499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
4647962f0357SSepherosa Ziehau 	struct ifnet *vf_ifp;
464840d60d6eSDexuan Cui 
464940d60d6eSDexuan Cui 	HN_LOCK(sc);
465040d60d6eSDexuan Cui 	vf_name[0] = '\0';
4651962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
4652962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
4653962f0357SSepherosa Ziehau 		snprintf(vf_name, sizeof(vf_name), "%s", vf_ifp->if_xname);
465440d60d6eSDexuan Cui 	HN_UNLOCK(sc);
465540d60d6eSDexuan Cui 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
465640d60d6eSDexuan Cui }
465740d60d6eSDexuan Cui 
465840d60d6eSDexuan Cui static int
4659499c3e17SSepherosa Ziehau hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS)
4660499c3e17SSepherosa Ziehau {
4661499c3e17SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4662499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
4663962f0357SSepherosa Ziehau 	struct ifnet *vf_ifp;
4664499c3e17SSepherosa Ziehau 
4665499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
4666499c3e17SSepherosa Ziehau 	vf_name[0] = '\0';
4667962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_rx_ring[0].hn_rxvf_ifp;
4668962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
4669962f0357SSepherosa Ziehau 		snprintf(vf_name, sizeof(vf_name), "%s", vf_ifp->if_xname);
4670499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
4671499c3e17SSepherosa Ziehau 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
4672499c3e17SSepherosa Ziehau }
4673499c3e17SSepherosa Ziehau 
4674499c3e17SSepherosa Ziehau static int
4675499c3e17SSepherosa Ziehau hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
4676499c3e17SSepherosa Ziehau {
4677499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4678499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4679499c3e17SSepherosa Ziehau 	int error, i;
4680499c3e17SSepherosa Ziehau 	bool first;
4681499c3e17SSepherosa Ziehau 
4682499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4683499c3e17SSepherosa Ziehau 	if (error != 0)
4684499c3e17SSepherosa Ziehau 		return (error);
4685499c3e17SSepherosa Ziehau 
4686499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4687499c3e17SSepherosa Ziehau 	if (sb == NULL)
4688499c3e17SSepherosa Ziehau 		return (ENOMEM);
4689499c3e17SSepherosa Ziehau 
4690499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4691499c3e17SSepherosa Ziehau 
4692499c3e17SSepherosa Ziehau 	first = true;
4693499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4694499c3e17SSepherosa Ziehau 		struct ifnet *ifp;
4695499c3e17SSepherosa Ziehau 
4696499c3e17SSepherosa Ziehau 		if (hn_vfmap[i] == NULL)
4697499c3e17SSepherosa Ziehau 			continue;
4698499c3e17SSepherosa Ziehau 
4699499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4700499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4701499c3e17SSepherosa Ziehau 			if (first)
4702499c3e17SSepherosa Ziehau 				sbuf_printf(sb, "%s", ifp->if_xname);
4703499c3e17SSepherosa Ziehau 			else
4704499c3e17SSepherosa Ziehau 				sbuf_printf(sb, " %s", ifp->if_xname);
4705499c3e17SSepherosa Ziehau 			first = false;
4706499c3e17SSepherosa Ziehau 		}
4707499c3e17SSepherosa Ziehau 	}
4708499c3e17SSepherosa Ziehau 
4709499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4710499c3e17SSepherosa Ziehau 
4711499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4712499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4713499c3e17SSepherosa Ziehau 	return (error);
4714499c3e17SSepherosa Ziehau }
4715499c3e17SSepherosa Ziehau 
4716499c3e17SSepherosa Ziehau static int
4717499c3e17SSepherosa Ziehau hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
4718499c3e17SSepherosa Ziehau {
4719499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4720499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4721499c3e17SSepherosa Ziehau 	int error, i;
4722499c3e17SSepherosa Ziehau 	bool first;
4723499c3e17SSepherosa Ziehau 
4724499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4725499c3e17SSepherosa Ziehau 	if (error != 0)
4726499c3e17SSepherosa Ziehau 		return (error);
4727499c3e17SSepherosa Ziehau 
4728499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4729499c3e17SSepherosa Ziehau 	if (sb == NULL)
4730499c3e17SSepherosa Ziehau 		return (ENOMEM);
4731499c3e17SSepherosa Ziehau 
4732499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4733499c3e17SSepherosa Ziehau 
4734499c3e17SSepherosa Ziehau 	first = true;
4735499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4736499c3e17SSepherosa Ziehau 		struct ifnet *ifp, *hn_ifp;
4737499c3e17SSepherosa Ziehau 
4738499c3e17SSepherosa Ziehau 		hn_ifp = hn_vfmap[i];
4739499c3e17SSepherosa Ziehau 		if (hn_ifp == NULL)
4740499c3e17SSepherosa Ziehau 			continue;
4741499c3e17SSepherosa Ziehau 
4742499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4743499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4744499c3e17SSepherosa Ziehau 			if (first) {
4745499c3e17SSepherosa Ziehau 				sbuf_printf(sb, "%s:%s", ifp->if_xname,
4746499c3e17SSepherosa Ziehau 				    hn_ifp->if_xname);
4747499c3e17SSepherosa Ziehau 			} else {
4748499c3e17SSepherosa Ziehau 				sbuf_printf(sb, " %s:%s", ifp->if_xname,
4749499c3e17SSepherosa Ziehau 				    hn_ifp->if_xname);
4750499c3e17SSepherosa Ziehau 			}
4751499c3e17SSepherosa Ziehau 			first = false;
4752499c3e17SSepherosa Ziehau 		}
4753499c3e17SSepherosa Ziehau 	}
4754499c3e17SSepherosa Ziehau 
4755499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4756499c3e17SSepherosa Ziehau 
4757499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4758499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4759499c3e17SSepherosa Ziehau 	return (error);
4760499c3e17SSepherosa Ziehau }
4761499c3e17SSepherosa Ziehau 
4762499c3e17SSepherosa Ziehau static int
47639c6cae24SSepherosa Ziehau hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS)
47649c6cae24SSepherosa Ziehau {
47659c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
47669c6cae24SSepherosa Ziehau 	int error, onoff = 0;
47679c6cae24SSepherosa Ziehau 
47689c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF)
47699c6cae24SSepherosa Ziehau 		onoff = 1;
47709c6cae24SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &onoff, 0, req);
47719c6cae24SSepherosa Ziehau 	if (error || req->newptr == NULL)
47729c6cae24SSepherosa Ziehau 		return (error);
47739c6cae24SSepherosa Ziehau 
47749c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
47759c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit() */
47769c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
47779c6cae24SSepherosa Ziehau 	if (onoff)
47789c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
47799c6cae24SSepherosa Ziehau 	else
47809c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags &= ~HN_XVFFLAG_ACCBPF;
47819c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
47829c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
47839c6cae24SSepherosa Ziehau 
47849c6cae24SSepherosa Ziehau 	return (0);
47859c6cae24SSepherosa Ziehau }
47869c6cae24SSepherosa Ziehau 
47879c6cae24SSepherosa Ziehau static int
47889c6cae24SSepherosa Ziehau hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS)
47899c6cae24SSepherosa Ziehau {
47909c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
47919c6cae24SSepherosa Ziehau 	int enabled = 0;
47929c6cae24SSepherosa Ziehau 
47939c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
47949c6cae24SSepherosa Ziehau 		enabled = 1;
47959c6cae24SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &enabled, 0, req));
47969c6cae24SSepherosa Ziehau }
47979c6cae24SSepherosa Ziehau 
47989c6cae24SSepherosa Ziehau static int
479915516c77SSepherosa Ziehau hn_check_iplen(const struct mbuf *m, int hoff)
480015516c77SSepherosa Ziehau {
480115516c77SSepherosa Ziehau 	const struct ip *ip;
480215516c77SSepherosa Ziehau 	int len, iphlen, iplen;
480315516c77SSepherosa Ziehau 	const struct tcphdr *th;
480415516c77SSepherosa Ziehau 	int thoff;				/* TCP data offset */
480515516c77SSepherosa Ziehau 
480615516c77SSepherosa Ziehau 	len = hoff + sizeof(struct ip);
480715516c77SSepherosa Ziehau 
480815516c77SSepherosa Ziehau 	/* The packet must be at least the size of an IP header. */
480915516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < len)
481015516c77SSepherosa Ziehau 		return IPPROTO_DONE;
481115516c77SSepherosa Ziehau 
481215516c77SSepherosa Ziehau 	/* The fixed IP header must reside completely in the first mbuf. */
481315516c77SSepherosa Ziehau 	if (m->m_len < len)
481415516c77SSepherosa Ziehau 		return IPPROTO_DONE;
481515516c77SSepherosa Ziehau 
481615516c77SSepherosa Ziehau 	ip = mtodo(m, hoff);
481715516c77SSepherosa Ziehau 
481815516c77SSepherosa Ziehau 	/* Bound check the packet's stated IP header length. */
481915516c77SSepherosa Ziehau 	iphlen = ip->ip_hl << 2;
482015516c77SSepherosa Ziehau 	if (iphlen < sizeof(struct ip))		/* minimum header length */
482115516c77SSepherosa Ziehau 		return IPPROTO_DONE;
482215516c77SSepherosa Ziehau 
482315516c77SSepherosa Ziehau 	/* The full IP header must reside completely in the one mbuf. */
482415516c77SSepherosa Ziehau 	if (m->m_len < hoff + iphlen)
482515516c77SSepherosa Ziehau 		return IPPROTO_DONE;
482615516c77SSepherosa Ziehau 
482715516c77SSepherosa Ziehau 	iplen = ntohs(ip->ip_len);
482815516c77SSepherosa Ziehau 
482915516c77SSepherosa Ziehau 	/*
483015516c77SSepherosa Ziehau 	 * Check that the amount of data in the buffers is as
483115516c77SSepherosa Ziehau 	 * at least much as the IP header would have us expect.
483215516c77SSepherosa Ziehau 	 */
483315516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < hoff + iplen)
483415516c77SSepherosa Ziehau 		return IPPROTO_DONE;
483515516c77SSepherosa Ziehau 
483615516c77SSepherosa Ziehau 	/*
483715516c77SSepherosa Ziehau 	 * Ignore IP fragments.
483815516c77SSepherosa Ziehau 	 */
483915516c77SSepherosa Ziehau 	if (ntohs(ip->ip_off) & (IP_OFFMASK | IP_MF))
484015516c77SSepherosa Ziehau 		return IPPROTO_DONE;
484115516c77SSepherosa Ziehau 
484215516c77SSepherosa Ziehau 	/*
484315516c77SSepherosa Ziehau 	 * The TCP/IP or UDP/IP header must be entirely contained within
484415516c77SSepherosa Ziehau 	 * the first fragment of a packet.
484515516c77SSepherosa Ziehau 	 */
484615516c77SSepherosa Ziehau 	switch (ip->ip_p) {
484715516c77SSepherosa Ziehau 	case IPPROTO_TCP:
484815516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct tcphdr))
484915516c77SSepherosa Ziehau 			return IPPROTO_DONE;
485015516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct tcphdr))
485115516c77SSepherosa Ziehau 			return IPPROTO_DONE;
485215516c77SSepherosa Ziehau 		th = (const struct tcphdr *)((const uint8_t *)ip + iphlen);
485315516c77SSepherosa Ziehau 		thoff = th->th_off << 2;
485415516c77SSepherosa Ziehau 		if (thoff < sizeof(struct tcphdr) || thoff + iphlen > iplen)
485515516c77SSepherosa Ziehau 			return IPPROTO_DONE;
485615516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + thoff)
485715516c77SSepherosa Ziehau 			return IPPROTO_DONE;
485815516c77SSepherosa Ziehau 		break;
485915516c77SSepherosa Ziehau 	case IPPROTO_UDP:
486015516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct udphdr))
486115516c77SSepherosa Ziehau 			return IPPROTO_DONE;
486215516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct udphdr))
486315516c77SSepherosa Ziehau 			return IPPROTO_DONE;
486415516c77SSepherosa Ziehau 		break;
486515516c77SSepherosa Ziehau 	default:
486615516c77SSepherosa Ziehau 		if (iplen < iphlen)
486715516c77SSepherosa Ziehau 			return IPPROTO_DONE;
486815516c77SSepherosa Ziehau 		break;
486915516c77SSepherosa Ziehau 	}
487015516c77SSepherosa Ziehau 	return ip->ip_p;
487115516c77SSepherosa Ziehau }
487215516c77SSepherosa Ziehau 
4873db76829bSSepherosa Ziehau static void
4874db76829bSSepherosa Ziehau hn_rxpkt_proto(const struct mbuf *m_new, int *l3proto, int *l4proto)
4875db76829bSSepherosa Ziehau {
4876db76829bSSepherosa Ziehau 	const struct ether_header *eh;
4877db76829bSSepherosa Ziehau 	uint16_t etype;
4878db76829bSSepherosa Ziehau 	int hoff;
4879db76829bSSepherosa Ziehau 
4880db76829bSSepherosa Ziehau 	hoff = sizeof(*eh);
4881db76829bSSepherosa Ziehau 	/* Checked at the beginning of this function. */
4882db76829bSSepherosa Ziehau 	KASSERT(m_new->m_len >= hoff, ("not ethernet frame"));
4883db76829bSSepherosa Ziehau 
4884db76829bSSepherosa Ziehau 	eh = mtod(m_new, const struct ether_header *);
4885db76829bSSepherosa Ziehau 	etype = ntohs(eh->ether_type);
4886db76829bSSepherosa Ziehau 	if (etype == ETHERTYPE_VLAN) {
4887db76829bSSepherosa Ziehau 		const struct ether_vlan_header *evl;
4888db76829bSSepherosa Ziehau 
4889db76829bSSepherosa Ziehau 		hoff = sizeof(*evl);
4890db76829bSSepherosa Ziehau 		if (m_new->m_len < hoff)
4891db76829bSSepherosa Ziehau 			return;
4892db76829bSSepherosa Ziehau 		evl = mtod(m_new, const struct ether_vlan_header *);
4893db76829bSSepherosa Ziehau 		etype = ntohs(evl->evl_proto);
4894db76829bSSepherosa Ziehau 	}
4895db76829bSSepherosa Ziehau 	*l3proto = etype;
4896db76829bSSepherosa Ziehau 
4897db76829bSSepherosa Ziehau 	if (etype == ETHERTYPE_IP)
4898db76829bSSepherosa Ziehau 		*l4proto = hn_check_iplen(m_new, hoff);
4899db76829bSSepherosa Ziehau 	else
4900db76829bSSepherosa Ziehau 		*l4proto = IPPROTO_DONE;
4901db76829bSSepherosa Ziehau }
4902db76829bSSepherosa Ziehau 
490315516c77SSepherosa Ziehau static int
490415516c77SSepherosa Ziehau hn_create_rx_data(struct hn_softc *sc, int ring_cnt)
490515516c77SSepherosa Ziehau {
490615516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
490715516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
490815516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
490915516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
491015516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
491115516c77SSepherosa Ziehau 	int lroent_cnt;
491215516c77SSepherosa Ziehau #endif
491315516c77SSepherosa Ziehau #endif
491415516c77SSepherosa Ziehau 	int i;
491515516c77SSepherosa Ziehau 
491615516c77SSepherosa Ziehau 	/*
491715516c77SSepherosa Ziehau 	 * Create RXBUF for reception.
491815516c77SSepherosa Ziehau 	 *
491915516c77SSepherosa Ziehau 	 * NOTE:
492015516c77SSepherosa Ziehau 	 * - It is shared by all channels.
492115516c77SSepherosa Ziehau 	 * - A large enough buffer is allocated, certain version of NVSes
492215516c77SSepherosa Ziehau 	 *   may further limit the usable space.
492315516c77SSepherosa Ziehau 	 */
492415516c77SSepherosa Ziehau 	sc->hn_rxbuf = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
492515516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_RXBUF_SIZE, &sc->hn_rxbuf_dma,
492615516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
492715516c77SSepherosa Ziehau 	if (sc->hn_rxbuf == NULL) {
492815516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate rxbuf failed\n");
492915516c77SSepherosa Ziehau 		return (ENOMEM);
493015516c77SSepherosa Ziehau 	}
493115516c77SSepherosa Ziehau 
493215516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = ring_cnt;
493315516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = sc->hn_rx_ring_cnt;
493415516c77SSepherosa Ziehau 
493515516c77SSepherosa Ziehau 	sc->hn_rx_ring = malloc(sizeof(struct hn_rx_ring) * sc->hn_rx_ring_cnt,
493615516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
493715516c77SSepherosa Ziehau 
493815516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
493915516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
494015516c77SSepherosa Ziehau 	lroent_cnt = hn_lro_entry_count;
494115516c77SSepherosa Ziehau 	if (lroent_cnt < TCP_LRO_ENTRIES)
494215516c77SSepherosa Ziehau 		lroent_cnt = TCP_LRO_ENTRIES;
494315516c77SSepherosa Ziehau 	if (bootverbose)
494415516c77SSepherosa Ziehau 		device_printf(dev, "LRO: entry count %d\n", lroent_cnt);
494515516c77SSepherosa Ziehau #endif
494615516c77SSepherosa Ziehau #endif	/* INET || INET6 */
494715516c77SSepherosa Ziehau 
494815516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
494915516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
495015516c77SSepherosa Ziehau 
495115516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.rx sysctl tree */
495215516c77SSepherosa Ziehau 	sc->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rx",
495315516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
495415516c77SSepherosa Ziehau 
495515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
495615516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
495715516c77SSepherosa Ziehau 
495815516c77SSepherosa Ziehau 		rxr->hn_br = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
495915516c77SSepherosa Ziehau 		    PAGE_SIZE, 0, HN_TXBR_SIZE + HN_RXBR_SIZE,
496015516c77SSepherosa Ziehau 		    &rxr->hn_br_dma, BUS_DMA_WAITOK);
496115516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL) {
496215516c77SSepherosa Ziehau 			device_printf(dev, "allocate bufring failed\n");
496315516c77SSepherosa Ziehau 			return (ENOMEM);
496415516c77SSepherosa Ziehau 		}
496515516c77SSepherosa Ziehau 
496615516c77SSepherosa Ziehau 		if (hn_trust_hosttcp)
496715516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_TCP;
496815516c77SSepherosa Ziehau 		if (hn_trust_hostudp)
496915516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_UDP;
497015516c77SSepherosa Ziehau 		if (hn_trust_hostip)
497115516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_IP;
4972642ec226SSepherosa Ziehau 		rxr->hn_mbuf_hash = NDIS_HASH_ALL;
497315516c77SSepherosa Ziehau 		rxr->hn_ifp = sc->hn_ifp;
497415516c77SSepherosa Ziehau 		if (i < sc->hn_tx_ring_cnt)
497515516c77SSepherosa Ziehau 			rxr->hn_txr = &sc->hn_tx_ring[i];
497615516c77SSepherosa Ziehau 		rxr->hn_pktbuf_len = HN_PKTBUF_LEN_DEF;
497715516c77SSepherosa Ziehau 		rxr->hn_pktbuf = malloc(rxr->hn_pktbuf_len, M_DEVBUF, M_WAITOK);
497815516c77SSepherosa Ziehau 		rxr->hn_rx_idx = i;
497915516c77SSepherosa Ziehau 		rxr->hn_rxbuf = sc->hn_rxbuf;
498015516c77SSepherosa Ziehau 
498115516c77SSepherosa Ziehau 		/*
498215516c77SSepherosa Ziehau 		 * Initialize LRO.
498315516c77SSepherosa Ziehau 		 */
498415516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
498515516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
498615516c77SSepherosa Ziehau 		tcp_lro_init_args(&rxr->hn_lro, sc->hn_ifp, lroent_cnt,
498715516c77SSepherosa Ziehau 		    hn_lro_mbufq_depth);
498815516c77SSepherosa Ziehau #else
498915516c77SSepherosa Ziehau 		tcp_lro_init(&rxr->hn_lro);
499015516c77SSepherosa Ziehau 		rxr->hn_lro.ifp = sc->hn_ifp;
499115516c77SSepherosa Ziehau #endif
499215516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
499315516c77SSepherosa Ziehau 		rxr->hn_lro.lro_length_lim = HN_LRO_LENLIM_DEF;
499415516c77SSepherosa Ziehau 		rxr->hn_lro.lro_ackcnt_lim = HN_LRO_ACKCNT_DEF;
499515516c77SSepherosa Ziehau #endif
499615516c77SSepherosa Ziehau #endif	/* INET || INET6 */
499715516c77SSepherosa Ziehau 
499815516c77SSepherosa Ziehau 		if (sc->hn_rx_sysctl_tree != NULL) {
499915516c77SSepherosa Ziehau 			char name[16];
500015516c77SSepherosa Ziehau 
500115516c77SSepherosa Ziehau 			/*
500215516c77SSepherosa Ziehau 			 * Create per RX ring sysctl tree:
500315516c77SSepherosa Ziehau 			 * dev.hn.UNIT.rx.RINGID
500415516c77SSepherosa Ziehau 			 */
500515516c77SSepherosa Ziehau 			snprintf(name, sizeof(name), "%d", i);
500615516c77SSepherosa Ziehau 			rxr->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx,
500715516c77SSepherosa Ziehau 			    SYSCTL_CHILDREN(sc->hn_rx_sysctl_tree),
500815516c77SSepherosa Ziehau 			    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
500915516c77SSepherosa Ziehau 
501015516c77SSepherosa Ziehau 			if (rxr->hn_rx_sysctl_tree != NULL) {
501115516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
501215516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
501315516c77SSepherosa Ziehau 				    OID_AUTO, "packets", CTLFLAG_RW,
501415516c77SSepherosa Ziehau 				    &rxr->hn_pkts, "# of packets received");
501515516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
501615516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
501715516c77SSepherosa Ziehau 				    OID_AUTO, "rss_pkts", CTLFLAG_RW,
501815516c77SSepherosa Ziehau 				    &rxr->hn_rss_pkts,
501915516c77SSepherosa Ziehau 				    "# of packets w/ RSS info received");
502015516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx,
502115516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
502215516c77SSepherosa Ziehau 				    OID_AUTO, "pktbuf_len", CTLFLAG_RD,
502315516c77SSepherosa Ziehau 				    &rxr->hn_pktbuf_len, 0,
502415516c77SSepherosa Ziehau 				    "Temporary channel packet buffer length");
502515516c77SSepherosa Ziehau 			}
502615516c77SSepherosa Ziehau 		}
502715516c77SSepherosa Ziehau 	}
502815516c77SSepherosa Ziehau 
502915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_queued",
503015516c77SSepherosa Ziehau 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
503115516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_queued),
503215516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
503315516c77SSepherosa Ziehau 	    hn_rx_stat_int_sysctl,
503415516c77SSepherosa Ziehau #else
503515516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
503615516c77SSepherosa Ziehau #endif
503715516c77SSepherosa Ziehau 	    "LU", "LRO queued");
503815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_flushed",
503915516c77SSepherosa Ziehau 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
504015516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_flushed),
504115516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
504215516c77SSepherosa Ziehau 	    hn_rx_stat_int_sysctl,
504315516c77SSepherosa Ziehau #else
504415516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
504515516c77SSepherosa Ziehau #endif
504615516c77SSepherosa Ziehau 	    "LU", "LRO flushed");
504715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_tried",
504815516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
504915516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro_tried),
505015516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of LRO tries");
505115516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
505215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_length_lim",
505315516c77SSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
505415516c77SSepherosa Ziehau 	    hn_lro_lenlim_sysctl, "IU",
505515516c77SSepherosa Ziehau 	    "Max # of data bytes to be aggregated by LRO");
505615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_ackcnt_lim",
505715516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
505815516c77SSepherosa Ziehau 	    hn_lro_ackcnt_sysctl, "I",
505915516c77SSepherosa Ziehau 	    "Max # of ACKs to be aggregated by LRO");
506015516c77SSepherosa Ziehau #endif
506115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hosttcp",
506215516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_TCP,
506315516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
506415516c77SSepherosa Ziehau 	    "Trust tcp segement verification on host side, "
506515516c77SSepherosa Ziehau 	    "when csum info is missing");
506615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostudp",
506715516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_UDP,
506815516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
506915516c77SSepherosa Ziehau 	    "Trust udp datagram verification on host side, "
507015516c77SSepherosa Ziehau 	    "when csum info is missing");
507115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostip",
507215516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_IP,
507315516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
507415516c77SSepherosa Ziehau 	    "Trust ip packet verification on host side, "
507515516c77SSepherosa Ziehau 	    "when csum info is missing");
507615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_ip",
507715516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
507815516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_ip),
507915516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM IP");
508015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_tcp",
508115516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
508215516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_tcp),
508315516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM TCP");
508415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_udp",
508515516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
508615516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_udp),
508715516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM UDP");
508815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_trusted",
508915516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
509015516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_trusted),
509115516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU",
509215516c77SSepherosa Ziehau 	    "# of packets that we trust host's csum verification");
509315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "small_pkts",
509415516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
509515516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_small_pkts),
509615516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of small packets received");
509715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_ack_failed",
509815516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
509915516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_ack_failed),
510015516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of RXBUF ack failures");
510115516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_cnt",
510215516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_cnt, 0, "# created RX rings");
510315516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_inuse",
510415516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_inuse, 0, "# used RX rings");
510515516c77SSepherosa Ziehau 
510615516c77SSepherosa Ziehau 	return (0);
510715516c77SSepherosa Ziehau }
510815516c77SSepherosa Ziehau 
510915516c77SSepherosa Ziehau static void
511015516c77SSepherosa Ziehau hn_destroy_rx_data(struct hn_softc *sc)
511115516c77SSepherosa Ziehau {
511215516c77SSepherosa Ziehau 	int i;
511315516c77SSepherosa Ziehau 
511415516c77SSepherosa Ziehau 	if (sc->hn_rxbuf != NULL) {
51152494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXBUF_REF) == 0)
511615516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_rxbuf_dma, sc->hn_rxbuf);
51172494d735SSepherosa Ziehau 		else
51182494d735SSepherosa Ziehau 			device_printf(sc->hn_dev, "RXBUF is referenced\n");
511915516c77SSepherosa Ziehau 		sc->hn_rxbuf = NULL;
512015516c77SSepherosa Ziehau 	}
512115516c77SSepherosa Ziehau 
512215516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_cnt == 0)
512315516c77SSepherosa Ziehau 		return;
512415516c77SSepherosa Ziehau 
512515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
512615516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
512715516c77SSepherosa Ziehau 
512815516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL)
512915516c77SSepherosa Ziehau 			continue;
51302494d735SSepherosa Ziehau 		if ((rxr->hn_rx_flags & HN_RX_FLAG_BR_REF) == 0) {
513115516c77SSepherosa Ziehau 			hyperv_dmamem_free(&rxr->hn_br_dma, rxr->hn_br);
51322494d735SSepherosa Ziehau 		} else {
51332494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
51342494d735SSepherosa Ziehau 			    "%dth channel bufring is referenced", i);
51352494d735SSepherosa Ziehau 		}
513615516c77SSepherosa Ziehau 		rxr->hn_br = NULL;
513715516c77SSepherosa Ziehau 
513815516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
513915516c77SSepherosa Ziehau 		tcp_lro_free(&rxr->hn_lro);
514015516c77SSepherosa Ziehau #endif
514115516c77SSepherosa Ziehau 		free(rxr->hn_pktbuf, M_DEVBUF);
514215516c77SSepherosa Ziehau 	}
514315516c77SSepherosa Ziehau 	free(sc->hn_rx_ring, M_DEVBUF);
514415516c77SSepherosa Ziehau 	sc->hn_rx_ring = NULL;
514515516c77SSepherosa Ziehau 
514615516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = 0;
514715516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = 0;
514815516c77SSepherosa Ziehau }
514915516c77SSepherosa Ziehau 
515015516c77SSepherosa Ziehau static int
515115516c77SSepherosa Ziehau hn_tx_ring_create(struct hn_softc *sc, int id)
515215516c77SSepherosa Ziehau {
515315516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[id];
515415516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
515515516c77SSepherosa Ziehau 	bus_dma_tag_t parent_dtag;
515615516c77SSepherosa Ziehau 	int error, i;
515715516c77SSepherosa Ziehau 
515815516c77SSepherosa Ziehau 	txr->hn_sc = sc;
515915516c77SSepherosa Ziehau 	txr->hn_tx_idx = id;
516015516c77SSepherosa Ziehau 
516115516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
516215516c77SSepherosa Ziehau 	mtx_init(&txr->hn_txlist_spin, "hn txlist", NULL, MTX_SPIN);
516315516c77SSepherosa Ziehau #endif
516415516c77SSepherosa Ziehau 	mtx_init(&txr->hn_tx_lock, "hn tx", NULL, MTX_DEF);
516515516c77SSepherosa Ziehau 
516615516c77SSepherosa Ziehau 	txr->hn_txdesc_cnt = HN_TX_DESC_CNT;
516715516c77SSepherosa Ziehau 	txr->hn_txdesc = malloc(sizeof(struct hn_txdesc) * txr->hn_txdesc_cnt,
516815516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
516915516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
517015516c77SSepherosa Ziehau 	SLIST_INIT(&txr->hn_txlist);
517115516c77SSepherosa Ziehau #else
517215516c77SSepherosa Ziehau 	txr->hn_txdesc_br = buf_ring_alloc(txr->hn_txdesc_cnt, M_DEVBUF,
517315516c77SSepherosa Ziehau 	    M_WAITOK, &txr->hn_tx_lock);
517415516c77SSepherosa Ziehau #endif
517515516c77SSepherosa Ziehau 
51760e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_EVTTQ) {
51770e11868dSSepherosa Ziehau 		txr->hn_tx_taskq = VMBUS_GET_EVENT_TASKQ(
51780e11868dSSepherosa Ziehau 		    device_get_parent(dev), dev, HN_RING_IDX2CPU(sc, id));
51790e11868dSSepherosa Ziehau 	} else {
5180fdd0222aSSepherosa Ziehau 		txr->hn_tx_taskq = sc->hn_tx_taskqs[id % hn_tx_taskq_cnt];
51810e11868dSSepherosa Ziehau 	}
518215516c77SSepherosa Ziehau 
518323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
518415516c77SSepherosa Ziehau 	if (hn_use_if_start) {
518515516c77SSepherosa Ziehau 		txr->hn_txeof = hn_start_txeof;
518615516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_start_taskfunc, txr);
518715516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_start_txeof_taskfunc, txr);
518823bf9e15SSepherosa Ziehau 	} else
518923bf9e15SSepherosa Ziehau #endif
519023bf9e15SSepherosa Ziehau 	{
519115516c77SSepherosa Ziehau 		int br_depth;
519215516c77SSepherosa Ziehau 
519315516c77SSepherosa Ziehau 		txr->hn_txeof = hn_xmit_txeof;
519415516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_xmit_taskfunc, txr);
519515516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_xmit_txeof_taskfunc, txr);
519615516c77SSepherosa Ziehau 
519715516c77SSepherosa Ziehau 		br_depth = hn_get_txswq_depth(txr);
519815516c77SSepherosa Ziehau 		txr->hn_mbuf_br = buf_ring_alloc(br_depth, M_DEVBUF,
519915516c77SSepherosa Ziehau 		    M_WAITOK, &txr->hn_tx_lock);
520015516c77SSepherosa Ziehau 	}
520115516c77SSepherosa Ziehau 
520215516c77SSepherosa Ziehau 	txr->hn_direct_tx_size = hn_direct_tx_size;
520315516c77SSepherosa Ziehau 
520415516c77SSepherosa Ziehau 	/*
520515516c77SSepherosa Ziehau 	 * Always schedule transmission instead of trying to do direct
520615516c77SSepherosa Ziehau 	 * transmission.  This one gives the best performance so far.
520715516c77SSepherosa Ziehau 	 */
520815516c77SSepherosa Ziehau 	txr->hn_sched_tx = 1;
520915516c77SSepherosa Ziehau 
521015516c77SSepherosa Ziehau 	parent_dtag = bus_get_dma_tag(dev);
521115516c77SSepherosa Ziehau 
521215516c77SSepherosa Ziehau 	/* DMA tag for RNDIS packet messages. */
521315516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
521415516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_ALIGN,		/* alignment */
521515516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_BOUNDARY,	/* boundary */
521615516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
521715516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
521815516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
521915516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsize */
522015516c77SSepherosa Ziehau 	    1,				/* nsegments */
522115516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsegsize */
522215516c77SSepherosa Ziehau 	    0,				/* flags */
522315516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
522415516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
522515516c77SSepherosa Ziehau 	    &txr->hn_tx_rndis_dtag);
522615516c77SSepherosa Ziehau 	if (error) {
522715516c77SSepherosa Ziehau 		device_printf(dev, "failed to create rndis dmatag\n");
522815516c77SSepherosa Ziehau 		return error;
522915516c77SSepherosa Ziehau 	}
523015516c77SSepherosa Ziehau 
523115516c77SSepherosa Ziehau 	/* DMA tag for data. */
523215516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
523315516c77SSepherosa Ziehau 	    1,				/* alignment */
523415516c77SSepherosa Ziehau 	    HN_TX_DATA_BOUNDARY,	/* boundary */
523515516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
523615516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
523715516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
523815516c77SSepherosa Ziehau 	    HN_TX_DATA_MAXSIZE,		/* maxsize */
523915516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGCNT_MAX,	/* nsegments */
524015516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGSIZE,		/* maxsegsize */
524115516c77SSepherosa Ziehau 	    0,				/* flags */
524215516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
524315516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
524415516c77SSepherosa Ziehau 	    &txr->hn_tx_data_dtag);
524515516c77SSepherosa Ziehau 	if (error) {
524615516c77SSepherosa Ziehau 		device_printf(dev, "failed to create data dmatag\n");
524715516c77SSepherosa Ziehau 		return error;
524815516c77SSepherosa Ziehau 	}
524915516c77SSepherosa Ziehau 
525015516c77SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i) {
525115516c77SSepherosa Ziehau 		struct hn_txdesc *txd = &txr->hn_txdesc[i];
525215516c77SSepherosa Ziehau 
525315516c77SSepherosa Ziehau 		txd->txr = txr;
525415516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
5255dc13fee6SSepherosa Ziehau 		STAILQ_INIT(&txd->agg_list);
525615516c77SSepherosa Ziehau 
525715516c77SSepherosa Ziehau 		/*
525815516c77SSepherosa Ziehau 		 * Allocate and load RNDIS packet message.
525915516c77SSepherosa Ziehau 		 */
526015516c77SSepherosa Ziehau         	error = bus_dmamem_alloc(txr->hn_tx_rndis_dtag,
526115516c77SSepherosa Ziehau 		    (void **)&txd->rndis_pkt,
526215516c77SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
526315516c77SSepherosa Ziehau 		    &txd->rndis_pkt_dmap);
526415516c77SSepherosa Ziehau 		if (error) {
526515516c77SSepherosa Ziehau 			device_printf(dev,
526615516c77SSepherosa Ziehau 			    "failed to allocate rndis_packet_msg, %d\n", i);
526715516c77SSepherosa Ziehau 			return error;
526815516c77SSepherosa Ziehau 		}
526915516c77SSepherosa Ziehau 
527015516c77SSepherosa Ziehau 		error = bus_dmamap_load(txr->hn_tx_rndis_dtag,
527115516c77SSepherosa Ziehau 		    txd->rndis_pkt_dmap,
527215516c77SSepherosa Ziehau 		    txd->rndis_pkt, HN_RNDIS_PKT_LEN,
527315516c77SSepherosa Ziehau 		    hyperv_dma_map_paddr, &txd->rndis_pkt_paddr,
527415516c77SSepherosa Ziehau 		    BUS_DMA_NOWAIT);
527515516c77SSepherosa Ziehau 		if (error) {
527615516c77SSepherosa Ziehau 			device_printf(dev,
527715516c77SSepherosa Ziehau 			    "failed to load rndis_packet_msg, %d\n", i);
527815516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
527915516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
528015516c77SSepherosa Ziehau 			return error;
528115516c77SSepherosa Ziehau 		}
528215516c77SSepherosa Ziehau 
528315516c77SSepherosa Ziehau 		/* DMA map for TX data. */
528415516c77SSepherosa Ziehau 		error = bus_dmamap_create(txr->hn_tx_data_dtag, 0,
528515516c77SSepherosa Ziehau 		    &txd->data_dmap);
528615516c77SSepherosa Ziehau 		if (error) {
528715516c77SSepherosa Ziehau 			device_printf(dev,
528815516c77SSepherosa Ziehau 			    "failed to allocate tx data dmamap\n");
528915516c77SSepherosa Ziehau 			bus_dmamap_unload(txr->hn_tx_rndis_dtag,
529015516c77SSepherosa Ziehau 			    txd->rndis_pkt_dmap);
529115516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
529215516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
529315516c77SSepherosa Ziehau 			return error;
529415516c77SSepherosa Ziehau 		}
529515516c77SSepherosa Ziehau 
529615516c77SSepherosa Ziehau 		/* All set, put it to list */
529715516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_ONLIST;
529815516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
529915516c77SSepherosa Ziehau 		SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
530015516c77SSepherosa Ziehau #else
530115516c77SSepherosa Ziehau 		buf_ring_enqueue(txr->hn_txdesc_br, txd);
530215516c77SSepherosa Ziehau #endif
530315516c77SSepherosa Ziehau 	}
530415516c77SSepherosa Ziehau 	txr->hn_txdesc_avail = txr->hn_txdesc_cnt;
530515516c77SSepherosa Ziehau 
530615516c77SSepherosa Ziehau 	if (sc->hn_tx_sysctl_tree != NULL) {
530715516c77SSepherosa Ziehau 		struct sysctl_oid_list *child;
530815516c77SSepherosa Ziehau 		struct sysctl_ctx_list *ctx;
530915516c77SSepherosa Ziehau 		char name[16];
531015516c77SSepherosa Ziehau 
531115516c77SSepherosa Ziehau 		/*
531215516c77SSepherosa Ziehau 		 * Create per TX ring sysctl tree:
531315516c77SSepherosa Ziehau 		 * dev.hn.UNIT.tx.RINGID
531415516c77SSepherosa Ziehau 		 */
531515516c77SSepherosa Ziehau 		ctx = device_get_sysctl_ctx(dev);
531615516c77SSepherosa Ziehau 		child = SYSCTL_CHILDREN(sc->hn_tx_sysctl_tree);
531715516c77SSepherosa Ziehau 
531815516c77SSepherosa Ziehau 		snprintf(name, sizeof(name), "%d", id);
531915516c77SSepherosa Ziehau 		txr->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
532015516c77SSepherosa Ziehau 		    name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
532115516c77SSepherosa Ziehau 
532215516c77SSepherosa Ziehau 		if (txr->hn_tx_sysctl_tree != NULL) {
532315516c77SSepherosa Ziehau 			child = SYSCTL_CHILDREN(txr->hn_tx_sysctl_tree);
532415516c77SSepherosa Ziehau 
532585e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
532615516c77SSepherosa Ziehau 			SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_avail",
532715516c77SSepherosa Ziehau 			    CTLFLAG_RD, &txr->hn_txdesc_avail, 0,
532815516c77SSepherosa Ziehau 			    "# of available TX descs");
532985e4ae1eSSepherosa Ziehau #endif
533023bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
533123bf9e15SSepherosa Ziehau 			if (!hn_use_if_start)
533223bf9e15SSepherosa Ziehau #endif
533323bf9e15SSepherosa Ziehau 			{
533415516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx, child, OID_AUTO, "oactive",
533515516c77SSepherosa Ziehau 				    CTLFLAG_RD, &txr->hn_oactive, 0,
533615516c77SSepherosa Ziehau 				    "over active");
533715516c77SSepherosa Ziehau 			}
533815516c77SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "packets",
533915516c77SSepherosa Ziehau 			    CTLFLAG_RW, &txr->hn_pkts,
534015516c77SSepherosa Ziehau 			    "# of packets transmitted");
5341dc13fee6SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "sends",
5342dc13fee6SSepherosa Ziehau 			    CTLFLAG_RW, &txr->hn_sends, "# of sends");
534315516c77SSepherosa Ziehau 		}
534415516c77SSepherosa Ziehau 	}
534515516c77SSepherosa Ziehau 
534615516c77SSepherosa Ziehau 	return 0;
534715516c77SSepherosa Ziehau }
534815516c77SSepherosa Ziehau 
534915516c77SSepherosa Ziehau static void
535015516c77SSepherosa Ziehau hn_txdesc_dmamap_destroy(struct hn_txdesc *txd)
535115516c77SSepherosa Ziehau {
535215516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = txd->txr;
535315516c77SSepherosa Ziehau 
535415516c77SSepherosa Ziehau 	KASSERT(txd->m == NULL, ("still has mbuf installed"));
535515516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("still dma mapped"));
535615516c77SSepherosa Ziehau 
535715516c77SSepherosa Ziehau 	bus_dmamap_unload(txr->hn_tx_rndis_dtag, txd->rndis_pkt_dmap);
535815516c77SSepherosa Ziehau 	bus_dmamem_free(txr->hn_tx_rndis_dtag, txd->rndis_pkt,
535915516c77SSepherosa Ziehau 	    txd->rndis_pkt_dmap);
536015516c77SSepherosa Ziehau 	bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap);
536115516c77SSepherosa Ziehau }
536215516c77SSepherosa Ziehau 
536315516c77SSepherosa Ziehau static void
536425641fc7SSepherosa Ziehau hn_txdesc_gc(struct hn_tx_ring *txr, struct hn_txdesc *txd)
536525641fc7SSepherosa Ziehau {
536625641fc7SSepherosa Ziehau 
536725641fc7SSepherosa Ziehau 	KASSERT(txd->refs == 0 || txd->refs == 1,
536825641fc7SSepherosa Ziehau 	    ("invalid txd refs %d", txd->refs));
536925641fc7SSepherosa Ziehau 
537025641fc7SSepherosa Ziehau 	/* Aggregated txds will be freed by their aggregating txd. */
537125641fc7SSepherosa Ziehau 	if (txd->refs > 0 && (txd->flags & HN_TXD_FLAG_ONAGG) == 0) {
537225641fc7SSepherosa Ziehau 		int freed;
537325641fc7SSepherosa Ziehau 
537425641fc7SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
537525641fc7SSepherosa Ziehau 		KASSERT(freed, ("can't free txdesc"));
537625641fc7SSepherosa Ziehau 	}
537725641fc7SSepherosa Ziehau }
537825641fc7SSepherosa Ziehau 
537925641fc7SSepherosa Ziehau static void
538015516c77SSepherosa Ziehau hn_tx_ring_destroy(struct hn_tx_ring *txr)
538115516c77SSepherosa Ziehau {
538225641fc7SSepherosa Ziehau 	int i;
538315516c77SSepherosa Ziehau 
538415516c77SSepherosa Ziehau 	if (txr->hn_txdesc == NULL)
538515516c77SSepherosa Ziehau 		return;
538615516c77SSepherosa Ziehau 
538725641fc7SSepherosa Ziehau 	/*
538825641fc7SSepherosa Ziehau 	 * NOTE:
538925641fc7SSepherosa Ziehau 	 * Because the freeing of aggregated txds will be deferred
539025641fc7SSepherosa Ziehau 	 * to the aggregating txd, two passes are used here:
539125641fc7SSepherosa Ziehau 	 * - The first pass GCes any pending txds.  This GC is necessary,
539225641fc7SSepherosa Ziehau 	 *   since if the channels are revoked, hypervisor will not
539325641fc7SSepherosa Ziehau 	 *   deliver send-done for all pending txds.
539425641fc7SSepherosa Ziehau 	 * - The second pass frees the busdma stuffs, i.e. after all txds
539525641fc7SSepherosa Ziehau 	 *   were freed.
539625641fc7SSepherosa Ziehau 	 */
539725641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
539825641fc7SSepherosa Ziehau 		hn_txdesc_gc(txr, &txr->hn_txdesc[i]);
539925641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
540025641fc7SSepherosa Ziehau 		hn_txdesc_dmamap_destroy(&txr->hn_txdesc[i]);
540115516c77SSepherosa Ziehau 
540215516c77SSepherosa Ziehau 	if (txr->hn_tx_data_dtag != NULL)
540315516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_data_dtag);
540415516c77SSepherosa Ziehau 	if (txr->hn_tx_rndis_dtag != NULL)
540515516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_rndis_dtag);
540615516c77SSepherosa Ziehau 
540715516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
540815516c77SSepherosa Ziehau 	buf_ring_free(txr->hn_txdesc_br, M_DEVBUF);
540915516c77SSepherosa Ziehau #endif
541015516c77SSepherosa Ziehau 
541115516c77SSepherosa Ziehau 	free(txr->hn_txdesc, M_DEVBUF);
541215516c77SSepherosa Ziehau 	txr->hn_txdesc = NULL;
541315516c77SSepherosa Ziehau 
541415516c77SSepherosa Ziehau 	if (txr->hn_mbuf_br != NULL)
541515516c77SSepherosa Ziehau 		buf_ring_free(txr->hn_mbuf_br, M_DEVBUF);
541615516c77SSepherosa Ziehau 
541715516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
541815516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_txlist_spin);
541915516c77SSepherosa Ziehau #endif
542015516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_tx_lock);
542115516c77SSepherosa Ziehau }
542215516c77SSepherosa Ziehau 
542315516c77SSepherosa Ziehau static int
542415516c77SSepherosa Ziehau hn_create_tx_data(struct hn_softc *sc, int ring_cnt)
542515516c77SSepherosa Ziehau {
542615516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
542715516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
542815516c77SSepherosa Ziehau 	int i;
542915516c77SSepherosa Ziehau 
543015516c77SSepherosa Ziehau 	/*
543115516c77SSepherosa Ziehau 	 * Create TXBUF for chimney sending.
543215516c77SSepherosa Ziehau 	 *
543315516c77SSepherosa Ziehau 	 * NOTE: It is shared by all channels.
543415516c77SSepherosa Ziehau 	 */
543515516c77SSepherosa Ziehau 	sc->hn_chim = hyperv_dmamem_alloc(bus_get_dma_tag(sc->hn_dev),
543615516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_CHIM_SIZE, &sc->hn_chim_dma,
543715516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
543815516c77SSepherosa Ziehau 	if (sc->hn_chim == NULL) {
543915516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate txbuf failed\n");
544015516c77SSepherosa Ziehau 		return (ENOMEM);
544115516c77SSepherosa Ziehau 	}
544215516c77SSepherosa Ziehau 
544315516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = ring_cnt;
544415516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
544515516c77SSepherosa Ziehau 
544615516c77SSepherosa Ziehau 	sc->hn_tx_ring = malloc(sizeof(struct hn_tx_ring) * sc->hn_tx_ring_cnt,
544715516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
544815516c77SSepherosa Ziehau 
544915516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(sc->hn_dev);
545015516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->hn_dev));
545115516c77SSepherosa Ziehau 
545215516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.tx sysctl tree */
545315516c77SSepherosa Ziehau 	sc->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tx",
545415516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
545515516c77SSepherosa Ziehau 
545615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
545715516c77SSepherosa Ziehau 		int error;
545815516c77SSepherosa Ziehau 
545915516c77SSepherosa Ziehau 		error = hn_tx_ring_create(sc, i);
546015516c77SSepherosa Ziehau 		if (error)
546115516c77SSepherosa Ziehau 			return error;
546215516c77SSepherosa Ziehau 	}
546315516c77SSepherosa Ziehau 
546415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "no_txdescs",
546515516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
546615516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_no_txdescs),
546715516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of times short of TX descs");
546815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "send_failed",
546915516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
547015516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_send_failed),
547115516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of hyper-v sending failure");
547215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdma_failed",
547315516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
547415516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_txdma_failed),
547515516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX DMA failure");
5476dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_flush_failed",
5477dc13fee6SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
5478dc13fee6SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_flush_failed),
5479dc13fee6SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU",
5480dc13fee6SSepherosa Ziehau 	    "# of packet transmission aggregation flush failure");
548115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_collapsed",
548215516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
548315516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_collapsed),
548415516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX mbuf collapsed");
548515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney",
548615516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
548715516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney),
548815516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send");
548915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_tried",
549015516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
549115516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney_tried),
549215516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send tries");
549315516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_cnt",
549415516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_txdesc_cnt, 0,
549515516c77SSepherosa Ziehau 	    "# of total TX descs");
549615516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_chimney_max",
549715516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_chim_szmax, 0,
549815516c77SSepherosa Ziehau 	    "Chimney send packet size upper boundary");
549915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_size",
550015516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
550115516c77SSepherosa Ziehau 	    hn_chim_size_sysctl, "I", "Chimney send packet size limit");
550215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "direct_tx_size",
550315516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
550415516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_direct_tx_size),
550515516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
550615516c77SSepherosa Ziehau 	    "Size of the packet for direct transmission");
550715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "sched_tx",
550815516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
550915516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_sched_tx),
551015516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
551115516c77SSepherosa Ziehau 	    "Always schedule transmission "
551215516c77SSepherosa Ziehau 	    "instead of doing direct transmission");
551315516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_cnt",
551415516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_cnt, 0, "# created TX rings");
551515516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_inuse",
551615516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_inuse, 0, "# used TX rings");
5517dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "agg_szmax",
5518dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_agg_szmax, 0,
5519dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation size");
5520dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pktmax",
5521dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5522dc13fee6SSepherosa Ziehau 	    hn_txagg_pktmax_sysctl, "I",
5523dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation packets");
5524dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_align",
5525dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5526dc13fee6SSepherosa Ziehau 	    hn_txagg_align_sysctl, "I",
5527dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation alignment");
552815516c77SSepherosa Ziehau 
552915516c77SSepherosa Ziehau 	return 0;
553015516c77SSepherosa Ziehau }
553115516c77SSepherosa Ziehau 
553215516c77SSepherosa Ziehau static void
553315516c77SSepherosa Ziehau hn_set_chim_size(struct hn_softc *sc, int chim_size)
553415516c77SSepherosa Ziehau {
553515516c77SSepherosa Ziehau 	int i;
553615516c77SSepherosa Ziehau 
5537a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
553815516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_chim_size = chim_size;
553915516c77SSepherosa Ziehau }
554015516c77SSepherosa Ziehau 
554115516c77SSepherosa Ziehau static void
554215516c77SSepherosa Ziehau hn_set_tso_maxsize(struct hn_softc *sc, int tso_maxlen, int mtu)
554315516c77SSepherosa Ziehau {
554415516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
55459c6cae24SSepherosa Ziehau 	u_int hw_tsomax;
554615516c77SSepherosa Ziehau 	int tso_minlen;
554715516c77SSepherosa Ziehau 
55489c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
55499c6cae24SSepherosa Ziehau 
555015516c77SSepherosa Ziehau 	if ((ifp->if_capabilities & (IFCAP_TSO4 | IFCAP_TSO6)) == 0)
555115516c77SSepherosa Ziehau 		return;
555215516c77SSepherosa Ziehau 
555315516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_sgmin >= 2,
555415516c77SSepherosa Ziehau 	    ("invalid NDIS tso sgmin %d", sc->hn_ndis_tso_sgmin));
555515516c77SSepherosa Ziehau 	tso_minlen = sc->hn_ndis_tso_sgmin * mtu;
555615516c77SSepherosa Ziehau 
555715516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_szmax >= tso_minlen &&
555815516c77SSepherosa Ziehau 	    sc->hn_ndis_tso_szmax <= IP_MAXPACKET,
555915516c77SSepherosa Ziehau 	    ("invalid NDIS tso szmax %d", sc->hn_ndis_tso_szmax));
556015516c77SSepherosa Ziehau 
556115516c77SSepherosa Ziehau 	if (tso_maxlen < tso_minlen)
556215516c77SSepherosa Ziehau 		tso_maxlen = tso_minlen;
556315516c77SSepherosa Ziehau 	else if (tso_maxlen > IP_MAXPACKET)
556415516c77SSepherosa Ziehau 		tso_maxlen = IP_MAXPACKET;
556515516c77SSepherosa Ziehau 	if (tso_maxlen > sc->hn_ndis_tso_szmax)
556615516c77SSepherosa Ziehau 		tso_maxlen = sc->hn_ndis_tso_szmax;
55679c6cae24SSepherosa Ziehau 	hw_tsomax = tso_maxlen - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
55689c6cae24SSepherosa Ziehau 
55699c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
55709c6cae24SSepherosa Ziehau 		if (hw_tsomax > sc->hn_vf_ifp->if_hw_tsomax)
55719c6cae24SSepherosa Ziehau 			hw_tsomax = sc->hn_vf_ifp->if_hw_tsomax;
55729c6cae24SSepherosa Ziehau 	}
55739c6cae24SSepherosa Ziehau 	ifp->if_hw_tsomax = hw_tsomax;
557415516c77SSepherosa Ziehau 	if (bootverbose)
557515516c77SSepherosa Ziehau 		if_printf(ifp, "TSO size max %u\n", ifp->if_hw_tsomax);
557615516c77SSepherosa Ziehau }
557715516c77SSepherosa Ziehau 
557815516c77SSepherosa Ziehau static void
557915516c77SSepherosa Ziehau hn_fixup_tx_data(struct hn_softc *sc)
558015516c77SSepherosa Ziehau {
558115516c77SSepherosa Ziehau 	uint64_t csum_assist;
558215516c77SSepherosa Ziehau 	int i;
558315516c77SSepherosa Ziehau 
558415516c77SSepherosa Ziehau 	hn_set_chim_size(sc, sc->hn_chim_szmax);
558515516c77SSepherosa Ziehau 	if (hn_tx_chimney_size > 0 &&
558615516c77SSepherosa Ziehau 	    hn_tx_chimney_size < sc->hn_chim_szmax)
558715516c77SSepherosa Ziehau 		hn_set_chim_size(sc, hn_tx_chimney_size);
558815516c77SSepherosa Ziehau 
558915516c77SSepherosa Ziehau 	csum_assist = 0;
559015516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_IPCS)
559115516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP;
559215516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP4CS)
559315516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_TCP;
55942be266caSSepherosa Ziehau 	if ((sc->hn_caps & HN_CAP_UDP4CS) && hn_enable_udp4cs)
559515516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_UDP;
559615516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP6CS)
559715516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_TCP;
55982be266caSSepherosa Ziehau 	if ((sc->hn_caps & HN_CAP_UDP6CS) && hn_enable_udp6cs)
559915516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_UDP;
560015516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
560115516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_csum_assist = csum_assist;
560215516c77SSepherosa Ziehau 
560315516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_HASHVAL) {
560415516c77SSepherosa Ziehau 		/*
560515516c77SSepherosa Ziehau 		 * Support HASHVAL pktinfo on TX path.
560615516c77SSepherosa Ziehau 		 */
560715516c77SSepherosa Ziehau 		if (bootverbose)
560815516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "support HASHVAL pktinfo\n");
560915516c77SSepherosa Ziehau 		for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
561015516c77SSepherosa Ziehau 			sc->hn_tx_ring[i].hn_tx_flags |= HN_TX_FLAG_HASHVAL;
561115516c77SSepherosa Ziehau 	}
561215516c77SSepherosa Ziehau }
561315516c77SSepherosa Ziehau 
561415516c77SSepherosa Ziehau static void
5615db76829bSSepherosa Ziehau hn_fixup_rx_data(struct hn_softc *sc)
5616db76829bSSepherosa Ziehau {
5617db76829bSSepherosa Ziehau 
5618db76829bSSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_UDPHASH) {
5619db76829bSSepherosa Ziehau 		int i;
5620db76829bSSepherosa Ziehau 
5621db76829bSSepherosa Ziehau 		for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
5622db76829bSSepherosa Ziehau 			sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_UDP_HASH;
5623db76829bSSepherosa Ziehau 	}
5624db76829bSSepherosa Ziehau }
5625db76829bSSepherosa Ziehau 
5626db76829bSSepherosa Ziehau static void
562715516c77SSepherosa Ziehau hn_destroy_tx_data(struct hn_softc *sc)
562815516c77SSepherosa Ziehau {
562915516c77SSepherosa Ziehau 	int i;
563015516c77SSepherosa Ziehau 
563115516c77SSepherosa Ziehau 	if (sc->hn_chim != NULL) {
56322494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_CHIM_REF) == 0) {
563315516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_chim_dma, sc->hn_chim);
56342494d735SSepherosa Ziehau 		} else {
56352494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
56362494d735SSepherosa Ziehau 			    "chimney sending buffer is referenced");
56372494d735SSepherosa Ziehau 		}
563815516c77SSepherosa Ziehau 		sc->hn_chim = NULL;
563915516c77SSepherosa Ziehau 	}
564015516c77SSepherosa Ziehau 
564115516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt == 0)
564215516c77SSepherosa Ziehau 		return;
564315516c77SSepherosa Ziehau 
564415516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
564515516c77SSepherosa Ziehau 		hn_tx_ring_destroy(&sc->hn_tx_ring[i]);
564615516c77SSepherosa Ziehau 
564715516c77SSepherosa Ziehau 	free(sc->hn_tx_ring, M_DEVBUF);
564815516c77SSepherosa Ziehau 	sc->hn_tx_ring = NULL;
564915516c77SSepherosa Ziehau 
565015516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = 0;
565115516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = 0;
565215516c77SSepherosa Ziehau }
565315516c77SSepherosa Ziehau 
565423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
565523bf9e15SSepherosa Ziehau 
565615516c77SSepherosa Ziehau static void
565715516c77SSepherosa Ziehau hn_start_taskfunc(void *xtxr, int pending __unused)
565815516c77SSepherosa Ziehau {
565915516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
566015516c77SSepherosa Ziehau 
566115516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
566215516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
566315516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
566415516c77SSepherosa Ziehau }
566515516c77SSepherosa Ziehau 
566623bf9e15SSepherosa Ziehau static int
566723bf9e15SSepherosa Ziehau hn_start_locked(struct hn_tx_ring *txr, int len)
566823bf9e15SSepherosa Ziehau {
566923bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
567023bf9e15SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
5671dc13fee6SSepherosa Ziehau 	int sched = 0;
567223bf9e15SSepherosa Ziehau 
567323bf9e15SSepherosa Ziehau 	KASSERT(hn_use_if_start,
567423bf9e15SSepherosa Ziehau 	    ("hn_start_locked is called, when if_start is disabled"));
567523bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
567623bf9e15SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
5677dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
567823bf9e15SSepherosa Ziehau 
567923bf9e15SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5680dc13fee6SSepherosa Ziehau 		return (0);
568123bf9e15SSepherosa Ziehau 
568223bf9e15SSepherosa Ziehau 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
568323bf9e15SSepherosa Ziehau 	    IFF_DRV_RUNNING)
5684dc13fee6SSepherosa Ziehau 		return (0);
568523bf9e15SSepherosa Ziehau 
568623bf9e15SSepherosa Ziehau 	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
568723bf9e15SSepherosa Ziehau 		struct hn_txdesc *txd;
568823bf9e15SSepherosa Ziehau 		struct mbuf *m_head;
568923bf9e15SSepherosa Ziehau 		int error;
569023bf9e15SSepherosa Ziehau 
569123bf9e15SSepherosa Ziehau 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
569223bf9e15SSepherosa Ziehau 		if (m_head == NULL)
569323bf9e15SSepherosa Ziehau 			break;
569423bf9e15SSepherosa Ziehau 
569523bf9e15SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
569623bf9e15SSepherosa Ziehau 			/*
569723bf9e15SSepherosa Ziehau 			 * This sending could be time consuming; let callers
569823bf9e15SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
569923bf9e15SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
570023bf9e15SSepherosa Ziehau 			 */
570123bf9e15SSepherosa Ziehau 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
5702dc13fee6SSepherosa Ziehau 			sched = 1;
5703dc13fee6SSepherosa Ziehau 			break;
570423bf9e15SSepherosa Ziehau 		}
570523bf9e15SSepherosa Ziehau 
5706edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
5707edd3f315SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
5708edd3f315SSepherosa Ziehau 			m_head = hn_tso_fixup(m_head);
5709edd3f315SSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5710edd3f315SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5711edd3f315SSepherosa Ziehau 				continue;
5712edd3f315SSepherosa Ziehau 			}
5713c49d47daSSepherosa Ziehau 		} else if (m_head->m_pkthdr.csum_flags &
5714c49d47daSSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP)) {
5715c49d47daSSepherosa Ziehau 			m_head = hn_set_hlen(m_head);
5716c49d47daSSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5717c49d47daSSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5718c49d47daSSepherosa Ziehau 				continue;
5719c49d47daSSepherosa Ziehau 			}
5720edd3f315SSepherosa Ziehau 		}
5721edd3f315SSepherosa Ziehau #endif
5722edd3f315SSepherosa Ziehau 
572323bf9e15SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
572423bf9e15SSepherosa Ziehau 		if (txd == NULL) {
572523bf9e15SSepherosa Ziehau 			txr->hn_no_txdescs++;
572623bf9e15SSepherosa Ziehau 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
572723bf9e15SSepherosa Ziehau 			atomic_set_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
572823bf9e15SSepherosa Ziehau 			break;
572923bf9e15SSepherosa Ziehau 		}
573023bf9e15SSepherosa Ziehau 
5731dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
573223bf9e15SSepherosa Ziehau 		if (error) {
573323bf9e15SSepherosa Ziehau 			/* Both txd and m_head are freed */
5734dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5735dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
573623bf9e15SSepherosa Ziehau 			continue;
573723bf9e15SSepherosa Ziehau 		}
573823bf9e15SSepherosa Ziehau 
5739dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5740dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5741dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5742dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5743dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
5744dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5745dc13fee6SSepherosa Ziehau 					atomic_set_int(&ifp->if_drv_flags,
5746dc13fee6SSepherosa Ziehau 					    IFF_DRV_OACTIVE);
5747dc13fee6SSepherosa Ziehau 					break;
5748dc13fee6SSepherosa Ziehau 				}
5749dc13fee6SSepherosa Ziehau 			} else {
5750dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
575123bf9e15SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
575223bf9e15SSepherosa Ziehau 				if (__predict_false(error)) {
575323bf9e15SSepherosa Ziehau 					/* txd is freed, but m_head is not */
575423bf9e15SSepherosa Ziehau 					IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
5755dc13fee6SSepherosa Ziehau 					atomic_set_int(&ifp->if_drv_flags,
5756dc13fee6SSepherosa Ziehau 					    IFF_DRV_OACTIVE);
575723bf9e15SSepherosa Ziehau 					break;
575823bf9e15SSepherosa Ziehau 				}
575923bf9e15SSepherosa Ziehau 			}
5760dc13fee6SSepherosa Ziehau 		}
5761dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5762dc13fee6SSepherosa Ziehau 		else {
5763dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5764dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5765dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5766dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5767dc13fee6SSepherosa Ziehau 		}
5768dc13fee6SSepherosa Ziehau #endif
5769dc13fee6SSepherosa Ziehau 	}
5770dc13fee6SSepherosa Ziehau 
5771dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5772dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5773dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5774dc13fee6SSepherosa Ziehau 	return (sched);
577523bf9e15SSepherosa Ziehau }
577623bf9e15SSepherosa Ziehau 
577723bf9e15SSepherosa Ziehau static void
577823bf9e15SSepherosa Ziehau hn_start(struct ifnet *ifp)
577923bf9e15SSepherosa Ziehau {
578023bf9e15SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
578123bf9e15SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[0];
578223bf9e15SSepherosa Ziehau 
578323bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
578423bf9e15SSepherosa Ziehau 		goto do_sched;
578523bf9e15SSepherosa Ziehau 
578623bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
578723bf9e15SSepherosa Ziehau 		int sched;
578823bf9e15SSepherosa Ziehau 
578923bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
579023bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
579123bf9e15SSepherosa Ziehau 		if (!sched)
579223bf9e15SSepherosa Ziehau 			return;
579323bf9e15SSepherosa Ziehau 	}
579423bf9e15SSepherosa Ziehau do_sched:
579523bf9e15SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
579623bf9e15SSepherosa Ziehau }
579723bf9e15SSepherosa Ziehau 
579815516c77SSepherosa Ziehau static void
579915516c77SSepherosa Ziehau hn_start_txeof_taskfunc(void *xtxr, int pending __unused)
580015516c77SSepherosa Ziehau {
580115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
580215516c77SSepherosa Ziehau 
580315516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
580415516c77SSepherosa Ziehau 	atomic_clear_int(&txr->hn_sc->hn_ifp->if_drv_flags, IFF_DRV_OACTIVE);
580515516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
580615516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
580715516c77SSepherosa Ziehau }
580815516c77SSepherosa Ziehau 
580923bf9e15SSepherosa Ziehau static void
581023bf9e15SSepherosa Ziehau hn_start_txeof(struct hn_tx_ring *txr)
581123bf9e15SSepherosa Ziehau {
581223bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
581323bf9e15SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
581423bf9e15SSepherosa Ziehau 
581523bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
581623bf9e15SSepherosa Ziehau 
581723bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
581823bf9e15SSepherosa Ziehau 		goto do_sched;
581923bf9e15SSepherosa Ziehau 
582023bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
582123bf9e15SSepherosa Ziehau 		int sched;
582223bf9e15SSepherosa Ziehau 
582323bf9e15SSepherosa Ziehau 		atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
582423bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
582523bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
582623bf9e15SSepherosa Ziehau 		if (sched) {
582723bf9e15SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
582823bf9e15SSepherosa Ziehau 			    &txr->hn_tx_task);
582923bf9e15SSepherosa Ziehau 		}
583023bf9e15SSepherosa Ziehau 	} else {
583123bf9e15SSepherosa Ziehau do_sched:
583223bf9e15SSepherosa Ziehau 		/*
583323bf9e15SSepherosa Ziehau 		 * Release the OACTIVE earlier, with the hope, that
583423bf9e15SSepherosa Ziehau 		 * others could catch up.  The task will clear the
583523bf9e15SSepherosa Ziehau 		 * flag again with the hn_tx_lock to avoid possible
583623bf9e15SSepherosa Ziehau 		 * races.
583723bf9e15SSepherosa Ziehau 		 */
583823bf9e15SSepherosa Ziehau 		atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
583923bf9e15SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
584023bf9e15SSepherosa Ziehau 	}
584123bf9e15SSepherosa Ziehau }
584223bf9e15SSepherosa Ziehau 
584323bf9e15SSepherosa Ziehau #endif	/* HN_IFSTART_SUPPORT */
584423bf9e15SSepherosa Ziehau 
584515516c77SSepherosa Ziehau static int
584615516c77SSepherosa Ziehau hn_xmit(struct hn_tx_ring *txr, int len)
584715516c77SSepherosa Ziehau {
584815516c77SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
584915516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
585015516c77SSepherosa Ziehau 	struct mbuf *m_head;
5851dc13fee6SSepherosa Ziehau 	int sched = 0;
585215516c77SSepherosa Ziehau 
585315516c77SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
585423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
585515516c77SSepherosa Ziehau 	KASSERT(hn_use_if_start == 0,
585615516c77SSepherosa Ziehau 	    ("hn_xmit is called, when if_start is enabled"));
585723bf9e15SSepherosa Ziehau #endif
5858dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
585915516c77SSepherosa Ziehau 
586015516c77SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5861dc13fee6SSepherosa Ziehau 		return (0);
586215516c77SSepherosa Ziehau 
586315516c77SSepherosa Ziehau 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || txr->hn_oactive)
5864dc13fee6SSepherosa Ziehau 		return (0);
586515516c77SSepherosa Ziehau 
586615516c77SSepherosa Ziehau 	while ((m_head = drbr_peek(ifp, txr->hn_mbuf_br)) != NULL) {
586715516c77SSepherosa Ziehau 		struct hn_txdesc *txd;
586815516c77SSepherosa Ziehau 		int error;
586915516c77SSepherosa Ziehau 
587015516c77SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
587115516c77SSepherosa Ziehau 			/*
587215516c77SSepherosa Ziehau 			 * This sending could be time consuming; let callers
587315516c77SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
587415516c77SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
587515516c77SSepherosa Ziehau 			 */
587615516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
5877dc13fee6SSepherosa Ziehau 			sched = 1;
5878dc13fee6SSepherosa Ziehau 			break;
587915516c77SSepherosa Ziehau 		}
588015516c77SSepherosa Ziehau 
588115516c77SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
588215516c77SSepherosa Ziehau 		if (txd == NULL) {
588315516c77SSepherosa Ziehau 			txr->hn_no_txdescs++;
588415516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
588515516c77SSepherosa Ziehau 			txr->hn_oactive = 1;
588615516c77SSepherosa Ziehau 			break;
588715516c77SSepherosa Ziehau 		}
588815516c77SSepherosa Ziehau 
5889dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
589015516c77SSepherosa Ziehau 		if (error) {
589115516c77SSepherosa Ziehau 			/* Both txd and m_head are freed; discard */
5892dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5893dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
589415516c77SSepherosa Ziehau 			drbr_advance(ifp, txr->hn_mbuf_br);
589515516c77SSepherosa Ziehau 			continue;
589615516c77SSepherosa Ziehau 		}
589715516c77SSepherosa Ziehau 
5898dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5899dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5900dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5901dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5902dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
590315516c77SSepherosa Ziehau 				if (__predict_false(error)) {
590415516c77SSepherosa Ziehau 					txr->hn_oactive = 1;
590515516c77SSepherosa Ziehau 					break;
590615516c77SSepherosa Ziehau 				}
5907dc13fee6SSepherosa Ziehau 			} else {
5908dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
5909dc13fee6SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
5910dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5911dc13fee6SSepherosa Ziehau 					/* txd is freed, but m_head is not */
5912dc13fee6SSepherosa Ziehau 					drbr_putback(ifp, txr->hn_mbuf_br,
5913dc13fee6SSepherosa Ziehau 					    m_head);
5914dc13fee6SSepherosa Ziehau 					txr->hn_oactive = 1;
5915dc13fee6SSepherosa Ziehau 					break;
5916dc13fee6SSepherosa Ziehau 				}
5917dc13fee6SSepherosa Ziehau 			}
5918dc13fee6SSepherosa Ziehau 		}
5919dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5920dc13fee6SSepherosa Ziehau 		else {
5921dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5922dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5923dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5924dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5925dc13fee6SSepherosa Ziehau 		}
5926dc13fee6SSepherosa Ziehau #endif
592715516c77SSepherosa Ziehau 
592815516c77SSepherosa Ziehau 		/* Sent */
592915516c77SSepherosa Ziehau 		drbr_advance(ifp, txr->hn_mbuf_br);
593015516c77SSepherosa Ziehau 	}
5931dc13fee6SSepherosa Ziehau 
5932dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5933dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5934dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5935dc13fee6SSepherosa Ziehau 	return (sched);
593615516c77SSepherosa Ziehau }
593715516c77SSepherosa Ziehau 
593815516c77SSepherosa Ziehau static int
593915516c77SSepherosa Ziehau hn_transmit(struct ifnet *ifp, struct mbuf *m)
594015516c77SSepherosa Ziehau {
594115516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
594215516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
594315516c77SSepherosa Ziehau 	int error, idx = 0;
594415516c77SSepherosa Ziehau 
59459c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
59469c6cae24SSepherosa Ziehau 		struct rm_priotracker pt;
59479c6cae24SSepherosa Ziehau 
59489c6cae24SSepherosa Ziehau 		rm_rlock(&sc->hn_vf_lock, &pt);
59499c6cae24SSepherosa Ziehau 		if (__predict_true(sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
59509c6cae24SSepherosa Ziehau 			struct mbuf *m_bpf = NULL;
59519c6cae24SSepherosa Ziehau 			int obytes, omcast;
59529c6cae24SSepherosa Ziehau 
59539c6cae24SSepherosa Ziehau 			obytes = m->m_pkthdr.len;
59547898a1f4SEric van Gyzen 			omcast = (m->m_flags & M_MCAST) != 0;
59559c6cae24SSepherosa Ziehau 
59569c6cae24SSepherosa Ziehau 			if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF) {
59579c6cae24SSepherosa Ziehau 				if (bpf_peers_present(ifp->if_bpf)) {
59589c6cae24SSepherosa Ziehau 					m_bpf = m_copypacket(m, M_NOWAIT);
59599c6cae24SSepherosa Ziehau 					if (m_bpf == NULL) {
59609c6cae24SSepherosa Ziehau 						/*
59619c6cae24SSepherosa Ziehau 						 * Failed to grab a shallow
59629c6cae24SSepherosa Ziehau 						 * copy; tap now.
59639c6cae24SSepherosa Ziehau 						 */
59649c6cae24SSepherosa Ziehau 						ETHER_BPF_MTAP(ifp, m);
59659c6cae24SSepherosa Ziehau 					}
59669c6cae24SSepherosa Ziehau 				}
59679c6cae24SSepherosa Ziehau 			} else {
59689c6cae24SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, m);
59699c6cae24SSepherosa Ziehau 			}
59709c6cae24SSepherosa Ziehau 
59719c6cae24SSepherosa Ziehau 			error = sc->hn_vf_ifp->if_transmit(sc->hn_vf_ifp, m);
59729c6cae24SSepherosa Ziehau 			rm_runlock(&sc->hn_vf_lock, &pt);
59739c6cae24SSepherosa Ziehau 
59749c6cae24SSepherosa Ziehau 			if (m_bpf != NULL) {
59759c6cae24SSepherosa Ziehau 				if (!error)
59769c6cae24SSepherosa Ziehau 					ETHER_BPF_MTAP(ifp, m_bpf);
59779c6cae24SSepherosa Ziehau 				m_freem(m_bpf);
59789c6cae24SSepherosa Ziehau 			}
59799c6cae24SSepherosa Ziehau 
59809c6cae24SSepherosa Ziehau 			if (error == ENOBUFS) {
59819c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
59829c6cae24SSepherosa Ziehau 			} else if (error) {
59839c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
59849c6cae24SSepherosa Ziehau 			} else {
59859c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
59869c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OBYTES, obytes);
59879c6cae24SSepherosa Ziehau 				if (omcast) {
59889c6cae24SSepherosa Ziehau 					if_inc_counter(ifp, IFCOUNTER_OMCASTS,
59899c6cae24SSepherosa Ziehau 					    omcast);
59909c6cae24SSepherosa Ziehau 				}
59919c6cae24SSepherosa Ziehau 			}
59929c6cae24SSepherosa Ziehau 			return (error);
59939c6cae24SSepherosa Ziehau 		}
59949c6cae24SSepherosa Ziehau 		rm_runlock(&sc->hn_vf_lock, &pt);
59959c6cae24SSepherosa Ziehau 	}
59969c6cae24SSepherosa Ziehau 
5997edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
5998edd3f315SSepherosa Ziehau 	/*
5999c49d47daSSepherosa Ziehau 	 * Perform TSO packet header fixup or get l2/l3 header length now,
6000c49d47daSSepherosa Ziehau 	 * since packet headers should be cache-hot.
6001edd3f315SSepherosa Ziehau 	 */
6002edd3f315SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
6003edd3f315SSepherosa Ziehau 		m = hn_tso_fixup(m);
6004edd3f315SSepherosa Ziehau 		if (__predict_false(m == NULL)) {
6005edd3f315SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
6006edd3f315SSepherosa Ziehau 			return EIO;
6007edd3f315SSepherosa Ziehau 		}
6008c49d47daSSepherosa Ziehau 	} else if (m->m_pkthdr.csum_flags &
6009c49d47daSSepherosa Ziehau 	    (CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP)) {
6010c49d47daSSepherosa Ziehau 		m = hn_set_hlen(m);
6011c49d47daSSepherosa Ziehau 		if (__predict_false(m == NULL)) {
6012c49d47daSSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
6013c49d47daSSepherosa Ziehau 			return EIO;
6014c49d47daSSepherosa Ziehau 		}
6015edd3f315SSepherosa Ziehau 	}
6016edd3f315SSepherosa Ziehau #endif
6017edd3f315SSepherosa Ziehau 
601815516c77SSepherosa Ziehau 	/*
601915516c77SSepherosa Ziehau 	 * Select the TX ring based on flowid
602015516c77SSepherosa Ziehau 	 */
602134d68912SSepherosa Ziehau 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
602234d68912SSepherosa Ziehau #ifdef RSS
602334d68912SSepherosa Ziehau 		uint32_t bid;
602434d68912SSepherosa Ziehau 
602534d68912SSepherosa Ziehau 		if (rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m),
602634d68912SSepherosa Ziehau 		    &bid) == 0)
602734d68912SSepherosa Ziehau 			idx = bid % sc->hn_tx_ring_inuse;
602834d68912SSepherosa Ziehau 		else
602934d68912SSepherosa Ziehau #endif
6030cc0c6ebcSSepherosa Ziehau 		{
6031cc0c6ebcSSepherosa Ziehau #if defined(INET6) || defined(INET)
6032cc0c6ebcSSepherosa Ziehau 			int tcpsyn = 0;
6033cc0c6ebcSSepherosa Ziehau 
6034cc0c6ebcSSepherosa Ziehau 			if (m->m_pkthdr.len < 128 &&
6035cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags &
6036cc0c6ebcSSepherosa Ziehau 			     (CSUM_IP_TCP | CSUM_IP6_TCP)) &&
6037cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
6038cc0c6ebcSSepherosa Ziehau 				m = hn_check_tcpsyn(m, &tcpsyn);
6039cc0c6ebcSSepherosa Ziehau 				if (__predict_false(m == NULL)) {
6040cc0c6ebcSSepherosa Ziehau 					if_inc_counter(ifp,
6041cc0c6ebcSSepherosa Ziehau 					    IFCOUNTER_OERRORS, 1);
6042cc0c6ebcSSepherosa Ziehau 					return (EIO);
6043cc0c6ebcSSepherosa Ziehau 				}
6044cc0c6ebcSSepherosa Ziehau 			}
6045cc0c6ebcSSepherosa Ziehau #else
6046cc0c6ebcSSepherosa Ziehau 			const int tcpsyn = 0;
6047cc0c6ebcSSepherosa Ziehau #endif
6048cc0c6ebcSSepherosa Ziehau 			if (tcpsyn)
6049cc0c6ebcSSepherosa Ziehau 				idx = 0;
6050cc0c6ebcSSepherosa Ziehau 			else
605115516c77SSepherosa Ziehau 				idx = m->m_pkthdr.flowid % sc->hn_tx_ring_inuse;
605234d68912SSepherosa Ziehau 		}
6053cc0c6ebcSSepherosa Ziehau 	}
605415516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[idx];
605515516c77SSepherosa Ziehau 
605615516c77SSepherosa Ziehau 	error = drbr_enqueue(ifp, txr->hn_mbuf_br, m);
605715516c77SSepherosa Ziehau 	if (error) {
605815516c77SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
605915516c77SSepherosa Ziehau 		return error;
606015516c77SSepherosa Ziehau 	}
606115516c77SSepherosa Ziehau 
606215516c77SSepherosa Ziehau 	if (txr->hn_oactive)
606315516c77SSepherosa Ziehau 		return 0;
606415516c77SSepherosa Ziehau 
606515516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
606615516c77SSepherosa Ziehau 		goto do_sched;
606715516c77SSepherosa Ziehau 
606815516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
606915516c77SSepherosa Ziehau 		int sched;
607015516c77SSepherosa Ziehau 
607115516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
607215516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
607315516c77SSepherosa Ziehau 		if (!sched)
607415516c77SSepherosa Ziehau 			return 0;
607515516c77SSepherosa Ziehau 	}
607615516c77SSepherosa Ziehau do_sched:
607715516c77SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
607815516c77SSepherosa Ziehau 	return 0;
607915516c77SSepherosa Ziehau }
608015516c77SSepherosa Ziehau 
608115516c77SSepherosa Ziehau static void
608215516c77SSepherosa Ziehau hn_tx_ring_qflush(struct hn_tx_ring *txr)
608315516c77SSepherosa Ziehau {
608415516c77SSepherosa Ziehau 	struct mbuf *m;
608515516c77SSepherosa Ziehau 
608615516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
608715516c77SSepherosa Ziehau 	while ((m = buf_ring_dequeue_sc(txr->hn_mbuf_br)) != NULL)
608815516c77SSepherosa Ziehau 		m_freem(m);
608915516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
609015516c77SSepherosa Ziehau }
609115516c77SSepherosa Ziehau 
609215516c77SSepherosa Ziehau static void
609315516c77SSepherosa Ziehau hn_xmit_qflush(struct ifnet *ifp)
609415516c77SSepherosa Ziehau {
609515516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
60969c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
609715516c77SSepherosa Ziehau 	int i;
609815516c77SSepherosa Ziehau 
609915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
610015516c77SSepherosa Ziehau 		hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
610115516c77SSepherosa Ziehau 	if_qflush(ifp);
61029c6cae24SSepherosa Ziehau 
61039c6cae24SSepherosa Ziehau 	rm_rlock(&sc->hn_vf_lock, &pt);
61049c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
61059c6cae24SSepherosa Ziehau 		sc->hn_vf_ifp->if_qflush(sc->hn_vf_ifp);
61069c6cae24SSepherosa Ziehau 	rm_runlock(&sc->hn_vf_lock, &pt);
610715516c77SSepherosa Ziehau }
610815516c77SSepherosa Ziehau 
610915516c77SSepherosa Ziehau static void
611015516c77SSepherosa Ziehau hn_xmit_txeof(struct hn_tx_ring *txr)
611115516c77SSepherosa Ziehau {
611215516c77SSepherosa Ziehau 
611315516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
611415516c77SSepherosa Ziehau 		goto do_sched;
611515516c77SSepherosa Ziehau 
611615516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
611715516c77SSepherosa Ziehau 		int sched;
611815516c77SSepherosa Ziehau 
611915516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
612015516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
612115516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
612215516c77SSepherosa Ziehau 		if (sched) {
612315516c77SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
612415516c77SSepherosa Ziehau 			    &txr->hn_tx_task);
612515516c77SSepherosa Ziehau 		}
612615516c77SSepherosa Ziehau 	} else {
612715516c77SSepherosa Ziehau do_sched:
612815516c77SSepherosa Ziehau 		/*
612915516c77SSepherosa Ziehau 		 * Release the oactive earlier, with the hope, that
613015516c77SSepherosa Ziehau 		 * others could catch up.  The task will clear the
613115516c77SSepherosa Ziehau 		 * oactive again with the hn_tx_lock to avoid possible
613215516c77SSepherosa Ziehau 		 * races.
613315516c77SSepherosa Ziehau 		 */
613415516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
613515516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
613615516c77SSepherosa Ziehau 	}
613715516c77SSepherosa Ziehau }
613815516c77SSepherosa Ziehau 
613915516c77SSepherosa Ziehau static void
614015516c77SSepherosa Ziehau hn_xmit_taskfunc(void *xtxr, int pending __unused)
614115516c77SSepherosa Ziehau {
614215516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
614315516c77SSepherosa Ziehau 
614415516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
614515516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
614615516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
614715516c77SSepherosa Ziehau }
614815516c77SSepherosa Ziehau 
614915516c77SSepherosa Ziehau static void
615015516c77SSepherosa Ziehau hn_xmit_txeof_taskfunc(void *xtxr, int pending __unused)
615115516c77SSepherosa Ziehau {
615215516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
615315516c77SSepherosa Ziehau 
615415516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
615515516c77SSepherosa Ziehau 	txr->hn_oactive = 0;
615615516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
615715516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
615815516c77SSepherosa Ziehau }
615915516c77SSepherosa Ziehau 
616015516c77SSepherosa Ziehau static int
616115516c77SSepherosa Ziehau hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
616215516c77SSepherosa Ziehau {
616315516c77SSepherosa Ziehau 	struct vmbus_chan_br cbr;
616415516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
616515516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = NULL;
616615516c77SSepherosa Ziehau 	int idx, error;
616715516c77SSepherosa Ziehau 
616815516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
616915516c77SSepherosa Ziehau 
617015516c77SSepherosa Ziehau 	/*
617115516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
617215516c77SSepherosa Ziehau 	 */
617315516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
617415516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
617515516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
617615516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
617715516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED) == 0,
617815516c77SSepherosa Ziehau 	    ("RX ring %d already attached", idx));
617915516c77SSepherosa Ziehau 	rxr->hn_rx_flags |= HN_RX_FLAG_ATTACHED;
61803ab0fea1SDexuan Cui 	rxr->hn_chan = chan;
618115516c77SSepherosa Ziehau 
618215516c77SSepherosa Ziehau 	if (bootverbose) {
618315516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "link RX ring %d to chan%u\n",
618415516c77SSepherosa Ziehau 		    idx, vmbus_chan_id(chan));
618515516c77SSepherosa Ziehau 	}
618615516c77SSepherosa Ziehau 
618715516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
618815516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[idx];
618915516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED) == 0,
619015516c77SSepherosa Ziehau 		    ("TX ring %d already attached", idx));
619115516c77SSepherosa Ziehau 		txr->hn_tx_flags |= HN_TX_FLAG_ATTACHED;
619215516c77SSepherosa Ziehau 
619315516c77SSepherosa Ziehau 		txr->hn_chan = chan;
619415516c77SSepherosa Ziehau 		if (bootverbose) {
619515516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "link TX ring %d to chan%u\n",
619615516c77SSepherosa Ziehau 			    idx, vmbus_chan_id(chan));
619715516c77SSepherosa Ziehau 		}
619815516c77SSepherosa Ziehau 	}
619915516c77SSepherosa Ziehau 
620015516c77SSepherosa Ziehau 	/* Bind this channel to a proper CPU. */
62010e11868dSSepherosa Ziehau 	vmbus_chan_cpu_set(chan, HN_RING_IDX2CPU(sc, idx));
620215516c77SSepherosa Ziehau 
620315516c77SSepherosa Ziehau 	/*
620415516c77SSepherosa Ziehau 	 * Open this channel
620515516c77SSepherosa Ziehau 	 */
620615516c77SSepherosa Ziehau 	cbr.cbr = rxr->hn_br;
620715516c77SSepherosa Ziehau 	cbr.cbr_paddr = rxr->hn_br_dma.hv_paddr;
620815516c77SSepherosa Ziehau 	cbr.cbr_txsz = HN_TXBR_SIZE;
620915516c77SSepherosa Ziehau 	cbr.cbr_rxsz = HN_RXBR_SIZE;
621015516c77SSepherosa Ziehau 	error = vmbus_chan_open_br(chan, &cbr, NULL, 0, hn_chan_callback, rxr);
621115516c77SSepherosa Ziehau 	if (error) {
621271e8ac56SSepherosa Ziehau 		if (error == EISCONN) {
621371e8ac56SSepherosa Ziehau 			if_printf(sc->hn_ifp, "bufring is connected after "
621471e8ac56SSepherosa Ziehau 			    "chan%u open failure\n", vmbus_chan_id(chan));
621571e8ac56SSepherosa Ziehau 			rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
621671e8ac56SSepherosa Ziehau 		} else {
621715516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "open chan%u failed: %d\n",
621815516c77SSepherosa Ziehau 			    vmbus_chan_id(chan), error);
621971e8ac56SSepherosa Ziehau 		}
622015516c77SSepherosa Ziehau 	}
622115516c77SSepherosa Ziehau 	return (error);
622215516c77SSepherosa Ziehau }
622315516c77SSepherosa Ziehau 
622415516c77SSepherosa Ziehau static void
622515516c77SSepherosa Ziehau hn_chan_detach(struct hn_softc *sc, struct vmbus_channel *chan)
622615516c77SSepherosa Ziehau {
622715516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
62282494d735SSepherosa Ziehau 	int idx, error;
622915516c77SSepherosa Ziehau 
623015516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
623115516c77SSepherosa Ziehau 
623215516c77SSepherosa Ziehau 	/*
623315516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
623415516c77SSepherosa Ziehau 	 */
623515516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
623615516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
623715516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
623815516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
623915516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED),
624015516c77SSepherosa Ziehau 	    ("RX ring %d is not attached", idx));
624115516c77SSepherosa Ziehau 	rxr->hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
624215516c77SSepherosa Ziehau 
624315516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
624415516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[idx];
624515516c77SSepherosa Ziehau 
624615516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED),
624715516c77SSepherosa Ziehau 		    ("TX ring %d is not attached attached", idx));
624815516c77SSepherosa Ziehau 		txr->hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
624915516c77SSepherosa Ziehau 	}
625015516c77SSepherosa Ziehau 
625115516c77SSepherosa Ziehau 	/*
625215516c77SSepherosa Ziehau 	 * Close this channel.
625315516c77SSepherosa Ziehau 	 *
625415516c77SSepherosa Ziehau 	 * NOTE:
625515516c77SSepherosa Ziehau 	 * Channel closing does _not_ destroy the target channel.
625615516c77SSepherosa Ziehau 	 */
62572494d735SSepherosa Ziehau 	error = vmbus_chan_close_direct(chan);
62582494d735SSepherosa Ziehau 	if (error == EISCONN) {
6259aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u bufring is connected "
6260aa1a2adcSSepherosa Ziehau 		    "after being closed\n", vmbus_chan_id(chan));
62612494d735SSepherosa Ziehau 		rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
62622494d735SSepherosa Ziehau 	} else if (error) {
6263aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u close failed: %d\n",
6264aa1a2adcSSepherosa Ziehau 		    vmbus_chan_id(chan), error);
62652494d735SSepherosa Ziehau 	}
626615516c77SSepherosa Ziehau }
626715516c77SSepherosa Ziehau 
626815516c77SSepherosa Ziehau static int
626915516c77SSepherosa Ziehau hn_attach_subchans(struct hn_softc *sc)
627015516c77SSepherosa Ziehau {
627115516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
627215516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
627315516c77SSepherosa Ziehau 	int i, error = 0;
627415516c77SSepherosa Ziehau 
627571e8ac56SSepherosa Ziehau 	KASSERT(subchan_cnt > 0, ("no sub-channels"));
627615516c77SSepherosa Ziehau 
627715516c77SSepherosa Ziehau 	/* Attach the sub-channels. */
627815516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
627915516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i) {
628071e8ac56SSepherosa Ziehau 		int error1;
628171e8ac56SSepherosa Ziehau 
628271e8ac56SSepherosa Ziehau 		error1 = hn_chan_attach(sc, subchans[i]);
628371e8ac56SSepherosa Ziehau 		if (error1) {
628471e8ac56SSepherosa Ziehau 			error = error1;
628571e8ac56SSepherosa Ziehau 			/* Move on; all channels will be detached later. */
628671e8ac56SSepherosa Ziehau 		}
628715516c77SSepherosa Ziehau 	}
628815516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
628915516c77SSepherosa Ziehau 
629015516c77SSepherosa Ziehau 	if (error) {
629115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "sub-channels attach failed: %d\n", error);
629215516c77SSepherosa Ziehau 	} else {
629315516c77SSepherosa Ziehau 		if (bootverbose) {
629415516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "%d sub-channels attached\n",
629515516c77SSepherosa Ziehau 			    subchan_cnt);
629615516c77SSepherosa Ziehau 		}
629715516c77SSepherosa Ziehau 	}
629815516c77SSepherosa Ziehau 	return (error);
629915516c77SSepherosa Ziehau }
630015516c77SSepherosa Ziehau 
630115516c77SSepherosa Ziehau static void
630215516c77SSepherosa Ziehau hn_detach_allchans(struct hn_softc *sc)
630315516c77SSepherosa Ziehau {
630415516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
630515516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
630615516c77SSepherosa Ziehau 	int i;
630715516c77SSepherosa Ziehau 
630815516c77SSepherosa Ziehau 	if (subchan_cnt == 0)
630915516c77SSepherosa Ziehau 		goto back;
631015516c77SSepherosa Ziehau 
631115516c77SSepherosa Ziehau 	/* Detach the sub-channels. */
631215516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
631315516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i)
631415516c77SSepherosa Ziehau 		hn_chan_detach(sc, subchans[i]);
631515516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
631615516c77SSepherosa Ziehau 
631715516c77SSepherosa Ziehau back:
631815516c77SSepherosa Ziehau 	/*
631915516c77SSepherosa Ziehau 	 * Detach the primary channel, _after_ all sub-channels
632015516c77SSepherosa Ziehau 	 * are detached.
632115516c77SSepherosa Ziehau 	 */
632215516c77SSepherosa Ziehau 	hn_chan_detach(sc, sc->hn_prichan);
632315516c77SSepherosa Ziehau 
632415516c77SSepherosa Ziehau 	/* Wait for sub-channels to be destroyed, if any. */
632515516c77SSepherosa Ziehau 	vmbus_subchan_drain(sc->hn_prichan);
632615516c77SSepherosa Ziehau 
632715516c77SSepherosa Ziehau #ifdef INVARIANTS
632815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
632915516c77SSepherosa Ziehau 		KASSERT((sc->hn_rx_ring[i].hn_rx_flags &
633015516c77SSepherosa Ziehau 		    HN_RX_FLAG_ATTACHED) == 0,
633115516c77SSepherosa Ziehau 		    ("%dth RX ring is still attached", i));
633215516c77SSepherosa Ziehau 	}
633315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
633415516c77SSepherosa Ziehau 		KASSERT((sc->hn_tx_ring[i].hn_tx_flags &
633515516c77SSepherosa Ziehau 		    HN_TX_FLAG_ATTACHED) == 0,
633615516c77SSepherosa Ziehau 		    ("%dth TX ring is still attached", i));
633715516c77SSepherosa Ziehau 	}
633815516c77SSepherosa Ziehau #endif
633915516c77SSepherosa Ziehau }
634015516c77SSepherosa Ziehau 
634115516c77SSepherosa Ziehau static int
634215516c77SSepherosa Ziehau hn_synth_alloc_subchans(struct hn_softc *sc, int *nsubch)
634315516c77SSepherosa Ziehau {
634415516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
634515516c77SSepherosa Ziehau 	int nchan, rxr_cnt, error;
634615516c77SSepherosa Ziehau 
634715516c77SSepherosa Ziehau 	nchan = *nsubch + 1;
634815516c77SSepherosa Ziehau 	if (nchan == 1) {
634915516c77SSepherosa Ziehau 		/*
635015516c77SSepherosa Ziehau 		 * Multiple RX/TX rings are not requested.
635115516c77SSepherosa Ziehau 		 */
635215516c77SSepherosa Ziehau 		*nsubch = 0;
635315516c77SSepherosa Ziehau 		return (0);
635415516c77SSepherosa Ziehau 	}
635515516c77SSepherosa Ziehau 
635615516c77SSepherosa Ziehau 	/*
635715516c77SSepherosa Ziehau 	 * Query RSS capabilities, e.g. # of RX rings, and # of indirect
635815516c77SSepherosa Ziehau 	 * table entries.
635915516c77SSepherosa Ziehau 	 */
636015516c77SSepherosa Ziehau 	error = hn_rndis_query_rsscaps(sc, &rxr_cnt);
636115516c77SSepherosa Ziehau 	if (error) {
636215516c77SSepherosa Ziehau 		/* No RSS; this is benign. */
636315516c77SSepherosa Ziehau 		*nsubch = 0;
636415516c77SSepherosa Ziehau 		return (0);
636515516c77SSepherosa Ziehau 	}
636615516c77SSepherosa Ziehau 	if (bootverbose) {
636715516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
636815516c77SSepherosa Ziehau 		    rxr_cnt, nchan);
636915516c77SSepherosa Ziehau 	}
637015516c77SSepherosa Ziehau 
637115516c77SSepherosa Ziehau 	if (nchan > rxr_cnt)
637215516c77SSepherosa Ziehau 		nchan = rxr_cnt;
637315516c77SSepherosa Ziehau 	if (nchan == 1) {
637415516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "only 1 channel is supported, no vRSS\n");
637515516c77SSepherosa Ziehau 		*nsubch = 0;
637615516c77SSepherosa Ziehau 		return (0);
637715516c77SSepherosa Ziehau 	}
637815516c77SSepherosa Ziehau 
637915516c77SSepherosa Ziehau 	/*
638015516c77SSepherosa Ziehau 	 * Allocate sub-channels from NVS.
638115516c77SSepherosa Ziehau 	 */
638215516c77SSepherosa Ziehau 	*nsubch = nchan - 1;
638315516c77SSepherosa Ziehau 	error = hn_nvs_alloc_subchans(sc, nsubch);
638415516c77SSepherosa Ziehau 	if (error || *nsubch == 0) {
638515516c77SSepherosa Ziehau 		/* Failed to allocate sub-channels. */
638615516c77SSepherosa Ziehau 		*nsubch = 0;
638715516c77SSepherosa Ziehau 		return (0);
638815516c77SSepherosa Ziehau 	}
638915516c77SSepherosa Ziehau 
639015516c77SSepherosa Ziehau 	/*
639115516c77SSepherosa Ziehau 	 * Wait for all sub-channels to become ready before moving on.
639215516c77SSepherosa Ziehau 	 */
639315516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, *nsubch);
639415516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, *nsubch);
639515516c77SSepherosa Ziehau 	return (0);
639615516c77SSepherosa Ziehau }
639715516c77SSepherosa Ziehau 
63982494d735SSepherosa Ziehau static bool
63992494d735SSepherosa Ziehau hn_synth_attachable(const struct hn_softc *sc)
64002494d735SSepherosa Ziehau {
64012494d735SSepherosa Ziehau 	int i;
64022494d735SSepherosa Ziehau 
64032494d735SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_ERRORS)
64042494d735SSepherosa Ziehau 		return (false);
64052494d735SSepherosa Ziehau 
64062494d735SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
64072494d735SSepherosa Ziehau 		const struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
64082494d735SSepherosa Ziehau 
64092494d735SSepherosa Ziehau 		if (rxr->hn_rx_flags & HN_RX_FLAG_BR_REF)
64102494d735SSepherosa Ziehau 			return (false);
64112494d735SSepherosa Ziehau 	}
64122494d735SSepherosa Ziehau 	return (true);
64132494d735SSepherosa Ziehau }
64142494d735SSepherosa Ziehau 
6415b3b75d9cSSepherosa Ziehau /*
6416b3b75d9cSSepherosa Ziehau  * Make sure that the RX filter is zero after the successful
6417b3b75d9cSSepherosa Ziehau  * RNDIS initialization.
6418b3b75d9cSSepherosa Ziehau  *
6419b3b75d9cSSepherosa Ziehau  * NOTE:
6420b3b75d9cSSepherosa Ziehau  * Under certain conditions on certain versions of Hyper-V,
6421b3b75d9cSSepherosa Ziehau  * the RNDIS rxfilter is _not_ zero on the hypervisor side
6422b3b75d9cSSepherosa Ziehau  * after the successful RNDIS initialization, which breaks
6423b3b75d9cSSepherosa Ziehau  * the assumption of any following code (well, it breaks the
6424b3b75d9cSSepherosa Ziehau  * RNDIS API contract actually).  Clear the RNDIS rxfilter
6425b3b75d9cSSepherosa Ziehau  * explicitly, drain packets sneaking through, and drain the
6426b3b75d9cSSepherosa Ziehau  * interrupt taskqueues scheduled due to the stealth packets.
6427b3b75d9cSSepherosa Ziehau  */
6428b3b75d9cSSepherosa Ziehau static void
6429b3b75d9cSSepherosa Ziehau hn_rndis_init_fixat(struct hn_softc *sc, int nchan)
6430b3b75d9cSSepherosa Ziehau {
6431b3b75d9cSSepherosa Ziehau 
6432b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
6433b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, nchan);
6434b3b75d9cSSepherosa Ziehau }
6435b3b75d9cSSepherosa Ziehau 
643615516c77SSepherosa Ziehau static int
643715516c77SSepherosa Ziehau hn_synth_attach(struct hn_softc *sc, int mtu)
643815516c77SSepherosa Ziehau {
643971e8ac56SSepherosa Ziehau #define ATTACHED_NVS		0x0002
644071e8ac56SSepherosa Ziehau #define ATTACHED_RNDIS		0x0004
644171e8ac56SSepherosa Ziehau 
644215516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
6443b3b75d9cSSepherosa Ziehau 	int error, nsubch, nchan = 1, i, rndis_inited;
644471e8ac56SSepherosa Ziehau 	uint32_t old_caps, attached = 0;
644515516c77SSepherosa Ziehau 
644615516c77SSepherosa Ziehau 	KASSERT((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0,
644715516c77SSepherosa Ziehau 	    ("synthetic parts were attached"));
644815516c77SSepherosa Ziehau 
64492494d735SSepherosa Ziehau 	if (!hn_synth_attachable(sc))
64502494d735SSepherosa Ziehau 		return (ENXIO);
64512494d735SSepherosa Ziehau 
645215516c77SSepherosa Ziehau 	/* Save capabilities for later verification. */
645315516c77SSepherosa Ziehau 	old_caps = sc->hn_caps;
645415516c77SSepherosa Ziehau 	sc->hn_caps = 0;
645515516c77SSepherosa Ziehau 
645615516c77SSepherosa Ziehau 	/* Clear RSS stuffs. */
645715516c77SSepherosa Ziehau 	sc->hn_rss_ind_size = 0;
645815516c77SSepherosa Ziehau 	sc->hn_rss_hash = 0;
6459642ec226SSepherosa Ziehau 	sc->hn_rss_hcap = 0;
646015516c77SSepherosa Ziehau 
646115516c77SSepherosa Ziehau 	/*
646215516c77SSepherosa Ziehau 	 * Attach the primary channel _before_ attaching NVS and RNDIS.
646315516c77SSepherosa Ziehau 	 */
646415516c77SSepherosa Ziehau 	error = hn_chan_attach(sc, sc->hn_prichan);
646515516c77SSepherosa Ziehau 	if (error)
646671e8ac56SSepherosa Ziehau 		goto failed;
646715516c77SSepherosa Ziehau 
646815516c77SSepherosa Ziehau 	/*
646915516c77SSepherosa Ziehau 	 * Attach NVS.
647015516c77SSepherosa Ziehau 	 */
647115516c77SSepherosa Ziehau 	error = hn_nvs_attach(sc, mtu);
647215516c77SSepherosa Ziehau 	if (error)
647371e8ac56SSepherosa Ziehau 		goto failed;
647471e8ac56SSepherosa Ziehau 	attached |= ATTACHED_NVS;
647515516c77SSepherosa Ziehau 
647615516c77SSepherosa Ziehau 	/*
647715516c77SSepherosa Ziehau 	 * Attach RNDIS _after_ NVS is attached.
647815516c77SSepherosa Ziehau 	 */
6479b3b75d9cSSepherosa Ziehau 	error = hn_rndis_attach(sc, mtu, &rndis_inited);
6480b3b75d9cSSepherosa Ziehau 	if (rndis_inited)
6481b3b75d9cSSepherosa Ziehau 		attached |= ATTACHED_RNDIS;
648215516c77SSepherosa Ziehau 	if (error)
648371e8ac56SSepherosa Ziehau 		goto failed;
648415516c77SSepherosa Ziehau 
648515516c77SSepherosa Ziehau 	/*
648615516c77SSepherosa Ziehau 	 * Make sure capabilities are not changed.
648715516c77SSepherosa Ziehau 	 */
648815516c77SSepherosa Ziehau 	if (device_is_attached(sc->hn_dev) && old_caps != sc->hn_caps) {
648915516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "caps mismatch old 0x%08x, new 0x%08x\n",
649015516c77SSepherosa Ziehau 		    old_caps, sc->hn_caps);
649171e8ac56SSepherosa Ziehau 		error = ENXIO;
649271e8ac56SSepherosa Ziehau 		goto failed;
649315516c77SSepherosa Ziehau 	}
649415516c77SSepherosa Ziehau 
649515516c77SSepherosa Ziehau 	/*
649615516c77SSepherosa Ziehau 	 * Allocate sub-channels for multi-TX/RX rings.
649715516c77SSepherosa Ziehau 	 *
649815516c77SSepherosa Ziehau 	 * NOTE:
649915516c77SSepherosa Ziehau 	 * The # of RX rings that can be used is equivalent to the # of
650015516c77SSepherosa Ziehau 	 * channels to be requested.
650115516c77SSepherosa Ziehau 	 */
650215516c77SSepherosa Ziehau 	nsubch = sc->hn_rx_ring_cnt - 1;
650315516c77SSepherosa Ziehau 	error = hn_synth_alloc_subchans(sc, &nsubch);
650415516c77SSepherosa Ziehau 	if (error)
650571e8ac56SSepherosa Ziehau 		goto failed;
650671e8ac56SSepherosa Ziehau 	/* NOTE: _Full_ synthetic parts detach is required now. */
650771e8ac56SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_SYNTH_ATTACHED;
650815516c77SSepherosa Ziehau 
650971e8ac56SSepherosa Ziehau 	/*
651071e8ac56SSepherosa Ziehau 	 * Set the # of TX/RX rings that could be used according to
651171e8ac56SSepherosa Ziehau 	 * the # of channels that NVS offered.
651271e8ac56SSepherosa Ziehau 	 */
651315516c77SSepherosa Ziehau 	nchan = nsubch + 1;
651471e8ac56SSepherosa Ziehau 	hn_set_ring_inuse(sc, nchan);
651515516c77SSepherosa Ziehau 	if (nchan == 1) {
651615516c77SSepherosa Ziehau 		/* Only the primary channel can be used; done */
651715516c77SSepherosa Ziehau 		goto back;
651815516c77SSepherosa Ziehau 	}
651915516c77SSepherosa Ziehau 
652015516c77SSepherosa Ziehau 	/*
652171e8ac56SSepherosa Ziehau 	 * Attach the sub-channels.
6522afd4971bSSepherosa Ziehau 	 *
6523afd4971bSSepherosa Ziehau 	 * NOTE: hn_set_ring_inuse() _must_ have been called.
652415516c77SSepherosa Ziehau 	 */
652571e8ac56SSepherosa Ziehau 	error = hn_attach_subchans(sc);
652671e8ac56SSepherosa Ziehau 	if (error)
652771e8ac56SSepherosa Ziehau 		goto failed;
652815516c77SSepherosa Ziehau 
652971e8ac56SSepherosa Ziehau 	/*
653071e8ac56SSepherosa Ziehau 	 * Configure RSS key and indirect table _after_ all sub-channels
653171e8ac56SSepherosa Ziehau 	 * are attached.
653271e8ac56SSepherosa Ziehau 	 */
653315516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSKEY) == 0) {
653415516c77SSepherosa Ziehau 		/*
653515516c77SSepherosa Ziehau 		 * RSS key is not set yet; set it to the default RSS key.
653615516c77SSepherosa Ziehau 		 */
653715516c77SSepherosa Ziehau 		if (bootverbose)
653815516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS key\n");
653934d68912SSepherosa Ziehau #ifdef RSS
654034d68912SSepherosa Ziehau 		rss_getkey(rss->rss_key);
654134d68912SSepherosa Ziehau #else
654215516c77SSepherosa Ziehau 		memcpy(rss->rss_key, hn_rss_key_default, sizeof(rss->rss_key));
654334d68912SSepherosa Ziehau #endif
654415516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
654515516c77SSepherosa Ziehau 	}
654615516c77SSepherosa Ziehau 
654715516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSIND) == 0) {
654815516c77SSepherosa Ziehau 		/*
654915516c77SSepherosa Ziehau 		 * RSS indirect table is not set yet; set it up in round-
655015516c77SSepherosa Ziehau 		 * robin fashion.
655115516c77SSepherosa Ziehau 		 */
655215516c77SSepherosa Ziehau 		if (bootverbose) {
655315516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS indirect "
655415516c77SSepherosa Ziehau 			    "table\n");
655515516c77SSepherosa Ziehau 		}
655634d68912SSepherosa Ziehau 		for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
655734d68912SSepherosa Ziehau 			uint32_t subidx;
655834d68912SSepherosa Ziehau 
655934d68912SSepherosa Ziehau #ifdef RSS
656034d68912SSepherosa Ziehau 			subidx = rss_get_indirection_to_bucket(i);
656134d68912SSepherosa Ziehau #else
656234d68912SSepherosa Ziehau 			subidx = i;
656334d68912SSepherosa Ziehau #endif
656434d68912SSepherosa Ziehau 			rss->rss_ind[i] = subidx % nchan;
656534d68912SSepherosa Ziehau 		}
656615516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSIND;
656715516c77SSepherosa Ziehau 	} else {
656815516c77SSepherosa Ziehau 		/*
656915516c77SSepherosa Ziehau 		 * # of usable channels may be changed, so we have to
657015516c77SSepherosa Ziehau 		 * make sure that all entries in RSS indirect table
657115516c77SSepherosa Ziehau 		 * are valid.
6572afd4971bSSepherosa Ziehau 		 *
6573afd4971bSSepherosa Ziehau 		 * NOTE: hn_set_ring_inuse() _must_ have been called.
657415516c77SSepherosa Ziehau 		 */
6575afd4971bSSepherosa Ziehau 		hn_rss_ind_fixup(sc);
657615516c77SSepherosa Ziehau 	}
657715516c77SSepherosa Ziehau 
6578642ec226SSepherosa Ziehau 	sc->hn_rss_hash = sc->hn_rss_hcap;
6579642ec226SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) ||
6580642ec226SSepherosa Ziehau 	    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
6581642ec226SSepherosa Ziehau 		/* NOTE: Don't reconfigure RSS; will do immediately. */
6582642ec226SSepherosa Ziehau 		hn_vf_rss_fixup(sc, false);
6583642ec226SSepherosa Ziehau 	}
658415516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
658515516c77SSepherosa Ziehau 	if (error)
658671e8ac56SSepherosa Ziehau 		goto failed;
658771e8ac56SSepherosa Ziehau back:
6588dc13fee6SSepherosa Ziehau 	/*
6589dc13fee6SSepherosa Ziehau 	 * Fixup transmission aggregation setup.
6590dc13fee6SSepherosa Ziehau 	 */
6591dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
6592b3b75d9cSSepherosa Ziehau 	hn_rndis_init_fixat(sc, nchan);
659315516c77SSepherosa Ziehau 	return (0);
659471e8ac56SSepherosa Ziehau 
659571e8ac56SSepherosa Ziehau failed:
659671e8ac56SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
6597b3b75d9cSSepherosa Ziehau 		hn_rndis_init_fixat(sc, nchan);
659871e8ac56SSepherosa Ziehau 		hn_synth_detach(sc);
659971e8ac56SSepherosa Ziehau 	} else {
6600b3b75d9cSSepherosa Ziehau 		if (attached & ATTACHED_RNDIS) {
6601b3b75d9cSSepherosa Ziehau 			hn_rndis_init_fixat(sc, nchan);
660271e8ac56SSepherosa Ziehau 			hn_rndis_detach(sc);
6603b3b75d9cSSepherosa Ziehau 		}
660471e8ac56SSepherosa Ziehau 		if (attached & ATTACHED_NVS)
660571e8ac56SSepherosa Ziehau 			hn_nvs_detach(sc);
660671e8ac56SSepherosa Ziehau 		hn_chan_detach(sc, sc->hn_prichan);
660771e8ac56SSepherosa Ziehau 		/* Restore old capabilities. */
660871e8ac56SSepherosa Ziehau 		sc->hn_caps = old_caps;
660971e8ac56SSepherosa Ziehau 	}
661071e8ac56SSepherosa Ziehau 	return (error);
661171e8ac56SSepherosa Ziehau 
661271e8ac56SSepherosa Ziehau #undef ATTACHED_RNDIS
661371e8ac56SSepherosa Ziehau #undef ATTACHED_NVS
661415516c77SSepherosa Ziehau }
661515516c77SSepherosa Ziehau 
661615516c77SSepherosa Ziehau /*
661715516c77SSepherosa Ziehau  * NOTE:
661815516c77SSepherosa Ziehau  * The interface must have been suspended though hn_suspend(), before
661915516c77SSepherosa Ziehau  * this function get called.
662015516c77SSepherosa Ziehau  */
662115516c77SSepherosa Ziehau static void
662215516c77SSepherosa Ziehau hn_synth_detach(struct hn_softc *sc)
662315516c77SSepherosa Ziehau {
662415516c77SSepherosa Ziehau 
662515516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
662615516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
662715516c77SSepherosa Ziehau 
662815516c77SSepherosa Ziehau 	/* Detach the RNDIS first. */
662915516c77SSepherosa Ziehau 	hn_rndis_detach(sc);
663015516c77SSepherosa Ziehau 
663115516c77SSepherosa Ziehau 	/* Detach NVS. */
663215516c77SSepherosa Ziehau 	hn_nvs_detach(sc);
663315516c77SSepherosa Ziehau 
663415516c77SSepherosa Ziehau 	/* Detach all of the channels. */
663515516c77SSepherosa Ziehau 	hn_detach_allchans(sc);
663615516c77SSepherosa Ziehau 
6637ace5ce7eSWei Hu 	if (vmbus_current_version >= VMBUS_VERSION_WIN10 && sc->hn_rxbuf_gpadl != 0) {
6638ace5ce7eSWei Hu 		/*
6639ace5ce7eSWei Hu 		 * Host is post-Win2016, disconnect RXBUF from primary channel here.
6640ace5ce7eSWei Hu 		 */
6641ace5ce7eSWei Hu 		int error;
6642ace5ce7eSWei Hu 
6643ace5ce7eSWei Hu 		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
6644ace5ce7eSWei Hu 		    sc->hn_rxbuf_gpadl);
6645ace5ce7eSWei Hu 		if (error) {
6646ace5ce7eSWei Hu 			if_printf(sc->hn_ifp,
6647ace5ce7eSWei Hu 			    "rxbuf gpadl disconn failed: %d\n", error);
6648ace5ce7eSWei Hu 			sc->hn_flags |= HN_FLAG_RXBUF_REF;
6649ace5ce7eSWei Hu 		}
6650ace5ce7eSWei Hu 		sc->hn_rxbuf_gpadl = 0;
6651ace5ce7eSWei Hu 	}
6652ace5ce7eSWei Hu 
6653ace5ce7eSWei Hu 	if (vmbus_current_version >= VMBUS_VERSION_WIN10 && sc->hn_chim_gpadl != 0) {
6654ace5ce7eSWei Hu 		/*
6655ace5ce7eSWei Hu 		 * Host is post-Win2016, disconnect chimney sending buffer from
6656ace5ce7eSWei Hu 		 * primary channel here.
6657ace5ce7eSWei Hu 		 */
6658ace5ce7eSWei Hu 		int error;
6659ace5ce7eSWei Hu 
6660ace5ce7eSWei Hu 		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
6661ace5ce7eSWei Hu 		    sc->hn_chim_gpadl);
6662ace5ce7eSWei Hu 		if (error) {
6663ace5ce7eSWei Hu 			if_printf(sc->hn_ifp,
6664ace5ce7eSWei Hu 			    "chim gpadl disconn failed: %d\n", error);
6665ace5ce7eSWei Hu 			sc->hn_flags |= HN_FLAG_CHIM_REF;
6666ace5ce7eSWei Hu 		}
6667ace5ce7eSWei Hu 		sc->hn_chim_gpadl = 0;
6668ace5ce7eSWei Hu 	}
666915516c77SSepherosa Ziehau 	sc->hn_flags &= ~HN_FLAG_SYNTH_ATTACHED;
667015516c77SSepherosa Ziehau }
667115516c77SSepherosa Ziehau 
667215516c77SSepherosa Ziehau static void
667315516c77SSepherosa Ziehau hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt)
667415516c77SSepherosa Ziehau {
667515516c77SSepherosa Ziehau 	KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_cnt,
667615516c77SSepherosa Ziehau 	    ("invalid ring count %d", ring_cnt));
667715516c77SSepherosa Ziehau 
667815516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt > ring_cnt)
667915516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = ring_cnt;
668015516c77SSepherosa Ziehau 	else
668115516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
668215516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = ring_cnt;
668315516c77SSepherosa Ziehau 
668434d68912SSepherosa Ziehau #ifdef RSS
668534d68912SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse != rss_getnumbuckets()) {
668634d68912SSepherosa Ziehau 		if_printf(sc->hn_ifp, "# of RX rings (%d) does not match "
668734d68912SSepherosa Ziehau 		    "# of RSS buckets (%d)\n", sc->hn_rx_ring_inuse,
668834d68912SSepherosa Ziehau 		    rss_getnumbuckets());
668934d68912SSepherosa Ziehau 	}
669034d68912SSepherosa Ziehau #endif
669134d68912SSepherosa Ziehau 
669215516c77SSepherosa Ziehau 	if (bootverbose) {
669315516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n",
669415516c77SSepherosa Ziehau 		    sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
669515516c77SSepherosa Ziehau 	}
669615516c77SSepherosa Ziehau }
669715516c77SSepherosa Ziehau 
669815516c77SSepherosa Ziehau static void
669925641fc7SSepherosa Ziehau hn_chan_drain(struct hn_softc *sc, struct vmbus_channel *chan)
670015516c77SSepherosa Ziehau {
670115516c77SSepherosa Ziehau 
670225641fc7SSepherosa Ziehau 	/*
670325641fc7SSepherosa Ziehau 	 * NOTE:
670425641fc7SSepherosa Ziehau 	 * The TX bufring will not be drained by the hypervisor,
670525641fc7SSepherosa Ziehau 	 * if the primary channel is revoked.
670625641fc7SSepherosa Ziehau 	 */
670725641fc7SSepherosa Ziehau 	while (!vmbus_chan_rx_empty(chan) ||
670825641fc7SSepherosa Ziehau 	    (!vmbus_chan_is_revoked(sc->hn_prichan) &&
670925641fc7SSepherosa Ziehau 	     !vmbus_chan_tx_empty(chan)))
671015516c77SSepherosa Ziehau 		pause("waitch", 1);
671115516c77SSepherosa Ziehau 	vmbus_chan_intr_drain(chan);
671215516c77SSepherosa Ziehau }
671315516c77SSepherosa Ziehau 
671415516c77SSepherosa Ziehau static void
6715b3b75d9cSSepherosa Ziehau hn_disable_rx(struct hn_softc *sc)
6716b3b75d9cSSepherosa Ziehau {
6717b3b75d9cSSepherosa Ziehau 
6718b3b75d9cSSepherosa Ziehau 	/*
6719b3b75d9cSSepherosa Ziehau 	 * Disable RX by clearing RX filter forcefully.
6720b3b75d9cSSepherosa Ziehau 	 */
6721b3b75d9cSSepherosa Ziehau 	sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE;
6722b3b75d9cSSepherosa Ziehau 	hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); /* ignore error */
6723b3b75d9cSSepherosa Ziehau 
6724b3b75d9cSSepherosa Ziehau 	/*
6725b3b75d9cSSepherosa Ziehau 	 * Give RNDIS enough time to flush all pending data packets.
6726b3b75d9cSSepherosa Ziehau 	 */
6727b3b75d9cSSepherosa Ziehau 	pause("waitrx", (200 * hz) / 1000);
6728b3b75d9cSSepherosa Ziehau }
6729b3b75d9cSSepherosa Ziehau 
6730b3b75d9cSSepherosa Ziehau /*
6731b3b75d9cSSepherosa Ziehau  * NOTE:
6732b3b75d9cSSepherosa Ziehau  * RX/TX _must_ have been suspended/disabled, before this function
6733b3b75d9cSSepherosa Ziehau  * is called.
6734b3b75d9cSSepherosa Ziehau  */
6735b3b75d9cSSepherosa Ziehau static void
6736b3b75d9cSSepherosa Ziehau hn_drain_rxtx(struct hn_softc *sc, int nchan)
673715516c77SSepherosa Ziehau {
673815516c77SSepherosa Ziehau 	struct vmbus_channel **subch = NULL;
6739b3b75d9cSSepherosa Ziehau 	int nsubch;
6740b3b75d9cSSepherosa Ziehau 
6741b3b75d9cSSepherosa Ziehau 	/*
6742b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX bufrings and interrupts.
6743b3b75d9cSSepherosa Ziehau 	 */
6744b3b75d9cSSepherosa Ziehau 	nsubch = nchan - 1;
6745b3b75d9cSSepherosa Ziehau 	if (nsubch > 0)
6746b3b75d9cSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
6747b3b75d9cSSepherosa Ziehau 
6748b3b75d9cSSepherosa Ziehau 	if (subch != NULL) {
6749b3b75d9cSSepherosa Ziehau 		int i;
6750b3b75d9cSSepherosa Ziehau 
6751b3b75d9cSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
6752b3b75d9cSSepherosa Ziehau 			hn_chan_drain(sc, subch[i]);
6753b3b75d9cSSepherosa Ziehau 	}
6754b3b75d9cSSepherosa Ziehau 	hn_chan_drain(sc, sc->hn_prichan);
6755b3b75d9cSSepherosa Ziehau 
6756b3b75d9cSSepherosa Ziehau 	if (subch != NULL)
6757b3b75d9cSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
6758b3b75d9cSSepherosa Ziehau }
6759b3b75d9cSSepherosa Ziehau 
6760b3b75d9cSSepherosa Ziehau static void
6761b3b75d9cSSepherosa Ziehau hn_suspend_data(struct hn_softc *sc)
6762b3b75d9cSSepherosa Ziehau {
676325641fc7SSepherosa Ziehau 	struct hn_tx_ring *txr;
6764b3b75d9cSSepherosa Ziehau 	int i;
676515516c77SSepherosa Ziehau 
676615516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
676715516c77SSepherosa Ziehau 
676815516c77SSepherosa Ziehau 	/*
676915516c77SSepherosa Ziehau 	 * Suspend TX.
677015516c77SSepherosa Ziehau 	 */
677115516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
677225641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
677315516c77SSepherosa Ziehau 
677415516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
677515516c77SSepherosa Ziehau 		txr->hn_suspended = 1;
677615516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
677715516c77SSepherosa Ziehau 		/* No one is able send more packets now. */
677815516c77SSepherosa Ziehau 
677925641fc7SSepherosa Ziehau 		/*
678025641fc7SSepherosa Ziehau 		 * Wait for all pending sends to finish.
678125641fc7SSepherosa Ziehau 		 *
678225641fc7SSepherosa Ziehau 		 * NOTE:
678325641fc7SSepherosa Ziehau 		 * We will _not_ receive all pending send-done, if the
678425641fc7SSepherosa Ziehau 		 * primary channel is revoked.
678525641fc7SSepherosa Ziehau 		 */
678625641fc7SSepherosa Ziehau 		while (hn_tx_ring_pending(txr) &&
678725641fc7SSepherosa Ziehau 		    !vmbus_chan_is_revoked(sc->hn_prichan))
678815516c77SSepherosa Ziehau 			pause("hnwtx", 1 /* 1 tick */);
678915516c77SSepherosa Ziehau 	}
679015516c77SSepherosa Ziehau 
679115516c77SSepherosa Ziehau 	/*
6792b3b75d9cSSepherosa Ziehau 	 * Disable RX.
679315516c77SSepherosa Ziehau 	 */
6794b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
679515516c77SSepherosa Ziehau 
679615516c77SSepherosa Ziehau 	/*
6797b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX.
679815516c77SSepherosa Ziehau 	 */
6799b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, sc->hn_rx_ring_inuse);
680025641fc7SSepherosa Ziehau 
680125641fc7SSepherosa Ziehau 	/*
680225641fc7SSepherosa Ziehau 	 * Drain any pending TX tasks.
680325641fc7SSepherosa Ziehau 	 *
680425641fc7SSepherosa Ziehau 	 * NOTE:
6805b3b75d9cSSepherosa Ziehau 	 * The above hn_drain_rxtx() can dispatch TX tasks, so the TX
6806b3b75d9cSSepherosa Ziehau 	 * tasks will have to be drained _after_ the above hn_drain_rxtx().
680725641fc7SSepherosa Ziehau 	 */
680825641fc7SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
680925641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
681025641fc7SSepherosa Ziehau 
681125641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task);
681225641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task);
681325641fc7SSepherosa Ziehau 	}
681415516c77SSepherosa Ziehau }
681515516c77SSepherosa Ziehau 
681615516c77SSepherosa Ziehau static void
681715516c77SSepherosa Ziehau hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused)
681815516c77SSepherosa Ziehau {
681915516c77SSepherosa Ziehau 
682015516c77SSepherosa Ziehau 	((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL;
682115516c77SSepherosa Ziehau }
682215516c77SSepherosa Ziehau 
682315516c77SSepherosa Ziehau static void
682415516c77SSepherosa Ziehau hn_suspend_mgmt(struct hn_softc *sc)
682515516c77SSepherosa Ziehau {
682615516c77SSepherosa Ziehau 	struct task task;
682715516c77SSepherosa Ziehau 
682815516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
682915516c77SSepherosa Ziehau 
683015516c77SSepherosa Ziehau 	/*
683115516c77SSepherosa Ziehau 	 * Make sure that hn_mgmt_taskq0 can nolonger be accessed
683215516c77SSepherosa Ziehau 	 * through hn_mgmt_taskq.
683315516c77SSepherosa Ziehau 	 */
683415516c77SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_suspend_mgmt_taskfunc, sc);
683515516c77SSepherosa Ziehau 	vmbus_chan_run_task(sc->hn_prichan, &task);
683615516c77SSepherosa Ziehau 
683715516c77SSepherosa Ziehau 	/*
683815516c77SSepherosa Ziehau 	 * Make sure that all pending management tasks are completed.
683915516c77SSepherosa Ziehau 	 */
684015516c77SSepherosa Ziehau 	taskqueue_drain(sc->hn_mgmt_taskq0, &sc->hn_netchg_init);
684115516c77SSepherosa Ziehau 	taskqueue_drain_timeout(sc->hn_mgmt_taskq0, &sc->hn_netchg_status);
684215516c77SSepherosa Ziehau 	taskqueue_drain_all(sc->hn_mgmt_taskq0);
684315516c77SSepherosa Ziehau }
684415516c77SSepherosa Ziehau 
684515516c77SSepherosa Ziehau static void
684615516c77SSepherosa Ziehau hn_suspend(struct hn_softc *sc)
684715516c77SSepherosa Ziehau {
684815516c77SSepherosa Ziehau 
684987f8129dSSepherosa Ziehau 	/* Disable polling. */
685087f8129dSSepherosa Ziehau 	hn_polling(sc, 0);
685187f8129dSSepherosa Ziehau 
68529c6cae24SSepherosa Ziehau 	/*
68539c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
68549c6cae24SSepherosa Ziehau 	 * device is receiving packets, so the data path of the
68559c6cae24SSepherosa Ziehau 	 * synthetic device must be suspended.
68569c6cae24SSepherosa Ziehau 	 */
68575bdfd3fdSDexuan Cui 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
6858962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
685915516c77SSepherosa Ziehau 		hn_suspend_data(sc);
686015516c77SSepherosa Ziehau 	hn_suspend_mgmt(sc);
686115516c77SSepherosa Ziehau }
686215516c77SSepherosa Ziehau 
686315516c77SSepherosa Ziehau static void
686415516c77SSepherosa Ziehau hn_resume_tx(struct hn_softc *sc, int tx_ring_cnt)
686515516c77SSepherosa Ziehau {
686615516c77SSepherosa Ziehau 	int i;
686715516c77SSepherosa Ziehau 
686815516c77SSepherosa Ziehau 	KASSERT(tx_ring_cnt <= sc->hn_tx_ring_cnt,
686915516c77SSepherosa Ziehau 	    ("invalid TX ring count %d", tx_ring_cnt));
687015516c77SSepherosa Ziehau 
687115516c77SSepherosa Ziehau 	for (i = 0; i < tx_ring_cnt; ++i) {
687215516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
687315516c77SSepherosa Ziehau 
687415516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
687515516c77SSepherosa Ziehau 		txr->hn_suspended = 0;
687615516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
687715516c77SSepherosa Ziehau 	}
687815516c77SSepherosa Ziehau }
687915516c77SSepherosa Ziehau 
688015516c77SSepherosa Ziehau static void
688115516c77SSepherosa Ziehau hn_resume_data(struct hn_softc *sc)
688215516c77SSepherosa Ziehau {
688315516c77SSepherosa Ziehau 	int i;
688415516c77SSepherosa Ziehau 
688515516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
688615516c77SSepherosa Ziehau 
688715516c77SSepherosa Ziehau 	/*
688815516c77SSepherosa Ziehau 	 * Re-enable RX.
688915516c77SSepherosa Ziehau 	 */
6890c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
689115516c77SSepherosa Ziehau 
689215516c77SSepherosa Ziehau 	/*
689315516c77SSepherosa Ziehau 	 * Make sure to clear suspend status on "all" TX rings,
689415516c77SSepherosa Ziehau 	 * since hn_tx_ring_inuse can be changed after
689515516c77SSepherosa Ziehau 	 * hn_suspend_data().
689615516c77SSepherosa Ziehau 	 */
689715516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_cnt);
689815516c77SSepherosa Ziehau 
689923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
690023bf9e15SSepherosa Ziehau 	if (!hn_use_if_start)
690123bf9e15SSepherosa Ziehau #endif
690223bf9e15SSepherosa Ziehau 	{
690315516c77SSepherosa Ziehau 		/*
690415516c77SSepherosa Ziehau 		 * Flush unused drbrs, since hn_tx_ring_inuse may be
690515516c77SSepherosa Ziehau 		 * reduced.
690615516c77SSepherosa Ziehau 		 */
690715516c77SSepherosa Ziehau 		for (i = sc->hn_tx_ring_inuse; i < sc->hn_tx_ring_cnt; ++i)
690815516c77SSepherosa Ziehau 			hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
690915516c77SSepherosa Ziehau 	}
691015516c77SSepherosa Ziehau 
691115516c77SSepherosa Ziehau 	/*
691215516c77SSepherosa Ziehau 	 * Kick start TX.
691315516c77SSepherosa Ziehau 	 */
691415516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
691515516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
691615516c77SSepherosa Ziehau 
691715516c77SSepherosa Ziehau 		/*
691815516c77SSepherosa Ziehau 		 * Use txeof task, so that any pending oactive can be
691915516c77SSepherosa Ziehau 		 * cleared properly.
692015516c77SSepherosa Ziehau 		 */
692115516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
692215516c77SSepherosa Ziehau 	}
692315516c77SSepherosa Ziehau }
692415516c77SSepherosa Ziehau 
692515516c77SSepherosa Ziehau static void
692615516c77SSepherosa Ziehau hn_resume_mgmt(struct hn_softc *sc)
692715516c77SSepherosa Ziehau {
692815516c77SSepherosa Ziehau 
692915516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
693015516c77SSepherosa Ziehau 
693115516c77SSepherosa Ziehau 	/*
693215516c77SSepherosa Ziehau 	 * Kick off network change detection, if it was pending.
693315516c77SSepherosa Ziehau 	 * If no network change was pending, start link status
693415516c77SSepherosa Ziehau 	 * checks, which is more lightweight than network change
693515516c77SSepherosa Ziehau 	 * detection.
693615516c77SSepherosa Ziehau 	 */
693715516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
693815516c77SSepherosa Ziehau 		hn_change_network(sc);
693915516c77SSepherosa Ziehau 	else
694015516c77SSepherosa Ziehau 		hn_update_link_status(sc);
694115516c77SSepherosa Ziehau }
694215516c77SSepherosa Ziehau 
694315516c77SSepherosa Ziehau static void
694415516c77SSepherosa Ziehau hn_resume(struct hn_softc *sc)
694515516c77SSepherosa Ziehau {
694615516c77SSepherosa Ziehau 
69479c6cae24SSepherosa Ziehau 	/*
69489c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
69499c6cae24SSepherosa Ziehau 	 * device have to receive packets, so the data path of the
69509c6cae24SSepherosa Ziehau 	 * synthetic device must be resumed.
69519c6cae24SSepherosa Ziehau 	 */
69525bdfd3fdSDexuan Cui 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
6953962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
695415516c77SSepherosa Ziehau 		hn_resume_data(sc);
69555bdfd3fdSDexuan Cui 
69565bdfd3fdSDexuan Cui 	/*
69579c6cae24SSepherosa Ziehau 	 * Don't resume link status change if VF is attached/activated.
69589c6cae24SSepherosa Ziehau 	 * - In the non-transparent VF mode, the synthetic device marks
69599c6cae24SSepherosa Ziehau 	 *   link down until the VF is deactivated; i.e. VF is down.
69609c6cae24SSepherosa Ziehau 	 * - In transparent VF mode, VF's media status is used until
69619c6cae24SSepherosa Ziehau 	 *   the VF is detached.
69625bdfd3fdSDexuan Cui 	 */
69639c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) == 0 &&
69649c6cae24SSepherosa Ziehau 	    !(hn_xpnt_vf && sc->hn_vf_ifp != NULL))
696515516c77SSepherosa Ziehau 		hn_resume_mgmt(sc);
696687f8129dSSepherosa Ziehau 
696787f8129dSSepherosa Ziehau 	/*
696887f8129dSSepherosa Ziehau 	 * Re-enable polling if this interface is running and
696987f8129dSSepherosa Ziehau 	 * the polling is requested.
697087f8129dSSepherosa Ziehau 	 */
697187f8129dSSepherosa Ziehau 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->hn_pollhz > 0)
697287f8129dSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
697315516c77SSepherosa Ziehau }
697415516c77SSepherosa Ziehau 
697515516c77SSepherosa Ziehau static void
697615516c77SSepherosa Ziehau hn_rndis_rx_status(struct hn_softc *sc, const void *data, int dlen)
697715516c77SSepherosa Ziehau {
697815516c77SSepherosa Ziehau 	const struct rndis_status_msg *msg;
697915516c77SSepherosa Ziehau 	int ofs;
698015516c77SSepherosa Ziehau 
698115516c77SSepherosa Ziehau 	if (dlen < sizeof(*msg)) {
698215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid RNDIS status\n");
698315516c77SSepherosa Ziehau 		return;
698415516c77SSepherosa Ziehau 	}
698515516c77SSepherosa Ziehau 	msg = data;
698615516c77SSepherosa Ziehau 
698715516c77SSepherosa Ziehau 	switch (msg->rm_status) {
698815516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_CONNECT:
698915516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_DISCONNECT:
699015516c77SSepherosa Ziehau 		hn_update_link_status(sc);
699115516c77SSepherosa Ziehau 		break;
699215516c77SSepherosa Ziehau 
699315516c77SSepherosa Ziehau 	case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
699440905afaSSepherosa Ziehau 	case RNDIS_STATUS_LINK_SPEED_CHANGE:
699515516c77SSepherosa Ziehau 		/* Not really useful; ignore. */
699615516c77SSepherosa Ziehau 		break;
699715516c77SSepherosa Ziehau 
699815516c77SSepherosa Ziehau 	case RNDIS_STATUS_NETWORK_CHANGE:
699915516c77SSepherosa Ziehau 		ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
700015516c77SSepherosa Ziehau 		if (dlen < ofs + msg->rm_stbuflen ||
700115516c77SSepherosa Ziehau 		    msg->rm_stbuflen < sizeof(uint32_t)) {
700215516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed\n");
700315516c77SSepherosa Ziehau 		} else {
700415516c77SSepherosa Ziehau 			uint32_t change;
700515516c77SSepherosa Ziehau 
700615516c77SSepherosa Ziehau 			memcpy(&change, ((const uint8_t *)msg) + ofs,
700715516c77SSepherosa Ziehau 			    sizeof(change));
700815516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed, change %u\n",
700915516c77SSepherosa Ziehau 			    change);
701015516c77SSepherosa Ziehau 		}
701115516c77SSepherosa Ziehau 		hn_change_network(sc);
701215516c77SSepherosa Ziehau 		break;
701315516c77SSepherosa Ziehau 
701415516c77SSepherosa Ziehau 	default:
701515516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
701615516c77SSepherosa Ziehau 		    msg->rm_status);
701715516c77SSepherosa Ziehau 		break;
701815516c77SSepherosa Ziehau 	}
701915516c77SSepherosa Ziehau }
702015516c77SSepherosa Ziehau 
702115516c77SSepherosa Ziehau static int
702215516c77SSepherosa Ziehau hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info)
702315516c77SSepherosa Ziehau {
702415516c77SSepherosa Ziehau 	const struct rndis_pktinfo *pi = info_data;
702515516c77SSepherosa Ziehau 	uint32_t mask = 0;
702615516c77SSepherosa Ziehau 
702715516c77SSepherosa Ziehau 	while (info_dlen != 0) {
702815516c77SSepherosa Ziehau 		const void *data;
702915516c77SSepherosa Ziehau 		uint32_t dlen;
703015516c77SSepherosa Ziehau 
703115516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < sizeof(*pi)))
703215516c77SSepherosa Ziehau 			return (EINVAL);
703315516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < pi->rm_size))
703415516c77SSepherosa Ziehau 			return (EINVAL);
703515516c77SSepherosa Ziehau 		info_dlen -= pi->rm_size;
703615516c77SSepherosa Ziehau 
703715516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
703815516c77SSepherosa Ziehau 			return (EINVAL);
703915516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
704015516c77SSepherosa Ziehau 			return (EINVAL);
704115516c77SSepherosa Ziehau 		dlen = pi->rm_size - pi->rm_pktinfooffset;
704215516c77SSepherosa Ziehau 		data = pi->rm_data;
704315516c77SSepherosa Ziehau 
704415516c77SSepherosa Ziehau 		switch (pi->rm_type) {
704515516c77SSepherosa Ziehau 		case NDIS_PKTINFO_TYPE_VLAN:
704615516c77SSepherosa Ziehau 			if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
704715516c77SSepherosa Ziehau 				return (EINVAL);
704815516c77SSepherosa Ziehau 			info->vlan_info = *((const uint32_t *)data);
704915516c77SSepherosa Ziehau 			mask |= HN_RXINFO_VLAN;
705015516c77SSepherosa Ziehau 			break;
705115516c77SSepherosa Ziehau 
705215516c77SSepherosa Ziehau 		case NDIS_PKTINFO_TYPE_CSUM:
705315516c77SSepherosa Ziehau 			if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
705415516c77SSepherosa Ziehau 				return (EINVAL);
705515516c77SSepherosa Ziehau 			info->csum_info = *((const uint32_t *)data);
705615516c77SSepherosa Ziehau 			mask |= HN_RXINFO_CSUM;
705715516c77SSepherosa Ziehau 			break;
705815516c77SSepherosa Ziehau 
705915516c77SSepherosa Ziehau 		case HN_NDIS_PKTINFO_TYPE_HASHVAL:
706015516c77SSepherosa Ziehau 			if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
706115516c77SSepherosa Ziehau 				return (EINVAL);
706215516c77SSepherosa Ziehau 			info->hash_value = *((const uint32_t *)data);
706315516c77SSepherosa Ziehau 			mask |= HN_RXINFO_HASHVAL;
706415516c77SSepherosa Ziehau 			break;
706515516c77SSepherosa Ziehau 
706615516c77SSepherosa Ziehau 		case HN_NDIS_PKTINFO_TYPE_HASHINF:
706715516c77SSepherosa Ziehau 			if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
706815516c77SSepherosa Ziehau 				return (EINVAL);
706915516c77SSepherosa Ziehau 			info->hash_info = *((const uint32_t *)data);
707015516c77SSepherosa Ziehau 			mask |= HN_RXINFO_HASHINF;
707115516c77SSepherosa Ziehau 			break;
707215516c77SSepherosa Ziehau 
707315516c77SSepherosa Ziehau 		default:
707415516c77SSepherosa Ziehau 			goto next;
707515516c77SSepherosa Ziehau 		}
707615516c77SSepherosa Ziehau 
707715516c77SSepherosa Ziehau 		if (mask == HN_RXINFO_ALL) {
707815516c77SSepherosa Ziehau 			/* All found; done */
707915516c77SSepherosa Ziehau 			break;
708015516c77SSepherosa Ziehau 		}
708115516c77SSepherosa Ziehau next:
708215516c77SSepherosa Ziehau 		pi = (const struct rndis_pktinfo *)
708315516c77SSepherosa Ziehau 		    ((const uint8_t *)pi + pi->rm_size);
708415516c77SSepherosa Ziehau 	}
708515516c77SSepherosa Ziehau 
708615516c77SSepherosa Ziehau 	/*
708715516c77SSepherosa Ziehau 	 * Final fixup.
708815516c77SSepherosa Ziehau 	 * - If there is no hash value, invalidate the hash info.
708915516c77SSepherosa Ziehau 	 */
709015516c77SSepherosa Ziehau 	if ((mask & HN_RXINFO_HASHVAL) == 0)
709115516c77SSepherosa Ziehau 		info->hash_info = HN_NDIS_HASH_INFO_INVALID;
709215516c77SSepherosa Ziehau 	return (0);
709315516c77SSepherosa Ziehau }
709415516c77SSepherosa Ziehau 
709515516c77SSepherosa Ziehau static __inline bool
709615516c77SSepherosa Ziehau hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
709715516c77SSepherosa Ziehau {
709815516c77SSepherosa Ziehau 
709915516c77SSepherosa Ziehau 	if (off < check_off) {
710015516c77SSepherosa Ziehau 		if (__predict_true(off + len <= check_off))
710115516c77SSepherosa Ziehau 			return (false);
710215516c77SSepherosa Ziehau 	} else if (off > check_off) {
710315516c77SSepherosa Ziehau 		if (__predict_true(check_off + check_len <= off))
710415516c77SSepherosa Ziehau 			return (false);
710515516c77SSepherosa Ziehau 	}
710615516c77SSepherosa Ziehau 	return (true);
710715516c77SSepherosa Ziehau }
710815516c77SSepherosa Ziehau 
710915516c77SSepherosa Ziehau static void
711015516c77SSepherosa Ziehau hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen)
711115516c77SSepherosa Ziehau {
711215516c77SSepherosa Ziehau 	const struct rndis_packet_msg *pkt;
711315516c77SSepherosa Ziehau 	struct hn_rxinfo info;
711415516c77SSepherosa Ziehau 	int data_off, pktinfo_off, data_len, pktinfo_len;
711515516c77SSepherosa Ziehau 
711615516c77SSepherosa Ziehau 	/*
711715516c77SSepherosa Ziehau 	 * Check length.
711815516c77SSepherosa Ziehau 	 */
711915516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*pkt))) {
712015516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
712115516c77SSepherosa Ziehau 		return;
712215516c77SSepherosa Ziehau 	}
712315516c77SSepherosa Ziehau 	pkt = data;
712415516c77SSepherosa Ziehau 
712515516c77SSepherosa Ziehau 	if (__predict_false(dlen < pkt->rm_len)) {
712615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
712715516c77SSepherosa Ziehau 		    "dlen %d, msglen %u\n", dlen, pkt->rm_len);
712815516c77SSepherosa Ziehau 		return;
712915516c77SSepherosa Ziehau 	}
713015516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_len <
713115516c77SSepherosa Ziehau 	    pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
713215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
713315516c77SSepherosa Ziehau 		    "msglen %u, data %u, oob %u, pktinfo %u\n",
713415516c77SSepherosa Ziehau 		    pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
713515516c77SSepherosa Ziehau 		    pkt->rm_pktinfolen);
713615516c77SSepherosa Ziehau 		return;
713715516c77SSepherosa Ziehau 	}
713815516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_datalen == 0)) {
713915516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
714015516c77SSepherosa Ziehau 		return;
714115516c77SSepherosa Ziehau 	}
714215516c77SSepherosa Ziehau 
714315516c77SSepherosa Ziehau 	/*
714415516c77SSepherosa Ziehau 	 * Check offests.
714515516c77SSepherosa Ziehau 	 */
714615516c77SSepherosa Ziehau #define IS_OFFSET_INVALID(ofs)			\
714715516c77SSepherosa Ziehau 	((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN ||	\
714815516c77SSepherosa Ziehau 	 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
714915516c77SSepherosa Ziehau 
715015516c77SSepherosa Ziehau 	/* XXX Hyper-V does not meet data offset alignment requirement */
715115516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
715215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
715315516c77SSepherosa Ziehau 		    "data offset %u\n", pkt->rm_dataoffset);
715415516c77SSepherosa Ziehau 		return;
715515516c77SSepherosa Ziehau 	}
715615516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdataoffset > 0 &&
715715516c77SSepherosa Ziehau 	    IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
715815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
715915516c77SSepherosa Ziehau 		    "oob offset %u\n", pkt->rm_oobdataoffset);
716015516c77SSepherosa Ziehau 		return;
716115516c77SSepherosa Ziehau 	}
716215516c77SSepherosa Ziehau 	if (__predict_true(pkt->rm_pktinfooffset > 0) &&
716315516c77SSepherosa Ziehau 	    __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
716415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
716515516c77SSepherosa Ziehau 		    "pktinfo offset %u\n", pkt->rm_pktinfooffset);
716615516c77SSepherosa Ziehau 		return;
716715516c77SSepherosa Ziehau 	}
716815516c77SSepherosa Ziehau 
716915516c77SSepherosa Ziehau #undef IS_OFFSET_INVALID
717015516c77SSepherosa Ziehau 
717115516c77SSepherosa Ziehau 	data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
717215516c77SSepherosa Ziehau 	data_len = pkt->rm_datalen;
717315516c77SSepherosa Ziehau 	pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
717415516c77SSepherosa Ziehau 	pktinfo_len = pkt->rm_pktinfolen;
717515516c77SSepherosa Ziehau 
717615516c77SSepherosa Ziehau 	/*
717715516c77SSepherosa Ziehau 	 * Check OOB coverage.
717815516c77SSepherosa Ziehau 	 */
717915516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdatalen != 0)) {
718015516c77SSepherosa Ziehau 		int oob_off, oob_len;
718115516c77SSepherosa Ziehau 
718215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "got oobdata\n");
718315516c77SSepherosa Ziehau 		oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
718415516c77SSepherosa Ziehau 		oob_len = pkt->rm_oobdatalen;
718515516c77SSepherosa Ziehau 
718615516c77SSepherosa Ziehau 		if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
718715516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
718815516c77SSepherosa Ziehau 			    "oob overflow, msglen %u, oob abs %d len %d\n",
718915516c77SSepherosa Ziehau 			    pkt->rm_len, oob_off, oob_len);
719015516c77SSepherosa Ziehau 			return;
719115516c77SSepherosa Ziehau 		}
719215516c77SSepherosa Ziehau 
719315516c77SSepherosa Ziehau 		/*
719415516c77SSepherosa Ziehau 		 * Check against data.
719515516c77SSepherosa Ziehau 		 */
719615516c77SSepherosa Ziehau 		if (hn_rndis_check_overlap(oob_off, oob_len,
719715516c77SSepherosa Ziehau 		    data_off, data_len)) {
719815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
719915516c77SSepherosa Ziehau 			    "oob overlaps data, oob abs %d len %d, "
720015516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
720115516c77SSepherosa Ziehau 			    oob_off, oob_len, data_off, data_len);
720215516c77SSepherosa Ziehau 			return;
720315516c77SSepherosa Ziehau 		}
720415516c77SSepherosa Ziehau 
720515516c77SSepherosa Ziehau 		/*
720615516c77SSepherosa Ziehau 		 * Check against pktinfo.
720715516c77SSepherosa Ziehau 		 */
720815516c77SSepherosa Ziehau 		if (pktinfo_len != 0 &&
720915516c77SSepherosa Ziehau 		    hn_rndis_check_overlap(oob_off, oob_len,
721015516c77SSepherosa Ziehau 		    pktinfo_off, pktinfo_len)) {
721115516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
721215516c77SSepherosa Ziehau 			    "oob overlaps pktinfo, oob abs %d len %d, "
721315516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
721415516c77SSepherosa Ziehau 			    oob_off, oob_len, pktinfo_off, pktinfo_len);
721515516c77SSepherosa Ziehau 			return;
721615516c77SSepherosa Ziehau 		}
721715516c77SSepherosa Ziehau 	}
721815516c77SSepherosa Ziehau 
721915516c77SSepherosa Ziehau 	/*
722015516c77SSepherosa Ziehau 	 * Check per-packet-info coverage and find useful per-packet-info.
722115516c77SSepherosa Ziehau 	 */
722215516c77SSepherosa Ziehau 	info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
722315516c77SSepherosa Ziehau 	info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
722415516c77SSepherosa Ziehau 	info.hash_info = HN_NDIS_HASH_INFO_INVALID;
722515516c77SSepherosa Ziehau 	if (__predict_true(pktinfo_len != 0)) {
722615516c77SSepherosa Ziehau 		bool overlap;
722715516c77SSepherosa Ziehau 		int error;
722815516c77SSepherosa Ziehau 
722915516c77SSepherosa Ziehau 		if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
723015516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
723115516c77SSepherosa Ziehau 			    "pktinfo overflow, msglen %u, "
723215516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
723315516c77SSepherosa Ziehau 			    pkt->rm_len, pktinfo_off, pktinfo_len);
723415516c77SSepherosa Ziehau 			return;
723515516c77SSepherosa Ziehau 		}
723615516c77SSepherosa Ziehau 
723715516c77SSepherosa Ziehau 		/*
723815516c77SSepherosa Ziehau 		 * Check packet info coverage.
723915516c77SSepherosa Ziehau 		 */
724015516c77SSepherosa Ziehau 		overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
724115516c77SSepherosa Ziehau 		    data_off, data_len);
724215516c77SSepherosa Ziehau 		if (__predict_false(overlap)) {
724315516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
724415516c77SSepherosa Ziehau 			    "pktinfo overlap data, pktinfo abs %d len %d, "
724515516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
724615516c77SSepherosa Ziehau 			    pktinfo_off, pktinfo_len, data_off, data_len);
724715516c77SSepherosa Ziehau 			return;
724815516c77SSepherosa Ziehau 		}
724915516c77SSepherosa Ziehau 
725015516c77SSepherosa Ziehau 		/*
725115516c77SSepherosa Ziehau 		 * Find useful per-packet-info.
725215516c77SSepherosa Ziehau 		 */
725315516c77SSepherosa Ziehau 		error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
725415516c77SSepherosa Ziehau 		    pktinfo_len, &info);
725515516c77SSepherosa Ziehau 		if (__predict_false(error)) {
725615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
725715516c77SSepherosa Ziehau 			    "pktinfo\n");
725815516c77SSepherosa Ziehau 			return;
725915516c77SSepherosa Ziehau 		}
726015516c77SSepherosa Ziehau 	}
726115516c77SSepherosa Ziehau 
726215516c77SSepherosa Ziehau 	if (__predict_false(data_off + data_len > pkt->rm_len)) {
726315516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
726415516c77SSepherosa Ziehau 		    "data overflow, msglen %u, data abs %d len %d\n",
726515516c77SSepherosa Ziehau 		    pkt->rm_len, data_off, data_len);
726615516c77SSepherosa Ziehau 		return;
726715516c77SSepherosa Ziehau 	}
726815516c77SSepherosa Ziehau 	hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
726915516c77SSepherosa Ziehau }
727015516c77SSepherosa Ziehau 
727115516c77SSepherosa Ziehau static __inline void
727215516c77SSepherosa Ziehau hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen)
727315516c77SSepherosa Ziehau {
727415516c77SSepherosa Ziehau 	const struct rndis_msghdr *hdr;
727515516c77SSepherosa Ziehau 
727615516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*hdr))) {
727715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
727815516c77SSepherosa Ziehau 		return;
727915516c77SSepherosa Ziehau 	}
728015516c77SSepherosa Ziehau 	hdr = data;
728115516c77SSepherosa Ziehau 
728215516c77SSepherosa Ziehau 	if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) {
728315516c77SSepherosa Ziehau 		/* Hot data path. */
728415516c77SSepherosa Ziehau 		hn_rndis_rx_data(rxr, data, dlen);
728515516c77SSepherosa Ziehau 		/* Done! */
728615516c77SSepherosa Ziehau 		return;
728715516c77SSepherosa Ziehau 	}
728815516c77SSepherosa Ziehau 
728915516c77SSepherosa Ziehau 	if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG)
729015516c77SSepherosa Ziehau 		hn_rndis_rx_status(rxr->hn_ifp->if_softc, data, dlen);
729115516c77SSepherosa Ziehau 	else
729215516c77SSepherosa Ziehau 		hn_rndis_rx_ctrl(rxr->hn_ifp->if_softc, data, dlen);
729315516c77SSepherosa Ziehau }
729415516c77SSepherosa Ziehau 
729515516c77SSepherosa Ziehau static void
729615516c77SSepherosa Ziehau hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
729715516c77SSepherosa Ziehau {
729815516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *hdr;
729915516c77SSepherosa Ziehau 
730015516c77SSepherosa Ziehau 	if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) {
730115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid nvs notify\n");
730215516c77SSepherosa Ziehau 		return;
730315516c77SSepherosa Ziehau 	}
730415516c77SSepherosa Ziehau 	hdr = VMBUS_CHANPKT_CONST_DATA(pkt);
730515516c77SSepherosa Ziehau 
730615516c77SSepherosa Ziehau 	if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) {
730715516c77SSepherosa Ziehau 		/* Useless; ignore */
730815516c77SSepherosa Ziehau 		return;
730915516c77SSepherosa Ziehau 	}
731015516c77SSepherosa Ziehau 	if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type);
731115516c77SSepherosa Ziehau }
731215516c77SSepherosa Ziehau 
731315516c77SSepherosa Ziehau static void
731415516c77SSepherosa Ziehau hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
731515516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkt)
731615516c77SSepherosa Ziehau {
731715516c77SSepherosa Ziehau 	struct hn_nvs_sendctx *sndc;
731815516c77SSepherosa Ziehau 
731915516c77SSepherosa Ziehau 	sndc = (struct hn_nvs_sendctx *)(uintptr_t)pkt->cph_xactid;
732015516c77SSepherosa Ziehau 	sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt),
732115516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_DATALEN(pkt));
732215516c77SSepherosa Ziehau 	/*
732315516c77SSepherosa Ziehau 	 * NOTE:
732415516c77SSepherosa Ziehau 	 * 'sndc' CAN NOT be accessed anymore, since it can be freed by
732515516c77SSepherosa Ziehau 	 * its callback.
732615516c77SSepherosa Ziehau 	 */
732715516c77SSepherosa Ziehau }
732815516c77SSepherosa Ziehau 
732915516c77SSepherosa Ziehau static void
733015516c77SSepherosa Ziehau hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
733115516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkthdr)
733215516c77SSepherosa Ziehau {
733315516c77SSepherosa Ziehau 	const struct vmbus_chanpkt_rxbuf *pkt;
733415516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *nvs_hdr;
733515516c77SSepherosa Ziehau 	int count, i, hlen;
733615516c77SSepherosa Ziehau 
733715516c77SSepherosa Ziehau 	if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) {
733815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n");
733915516c77SSepherosa Ziehau 		return;
734015516c77SSepherosa Ziehau 	}
734115516c77SSepherosa Ziehau 	nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr);
734215516c77SSepherosa Ziehau 
734315516c77SSepherosa Ziehau 	/* Make sure that this is a RNDIS message. */
734415516c77SSepherosa Ziehau 	if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) {
734515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n",
734615516c77SSepherosa Ziehau 		    nvs_hdr->nvs_type);
734715516c77SSepherosa Ziehau 		return;
734815516c77SSepherosa Ziehau 	}
734915516c77SSepherosa Ziehau 
735015516c77SSepherosa Ziehau 	hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen);
735115516c77SSepherosa Ziehau 	if (__predict_false(hlen < sizeof(*pkt))) {
735215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n");
735315516c77SSepherosa Ziehau 		return;
735415516c77SSepherosa Ziehau 	}
735515516c77SSepherosa Ziehau 	pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr;
735615516c77SSepherosa Ziehau 
735715516c77SSepherosa Ziehau 	if (__predict_false(pkt->cp_rxbuf_id != HN_NVS_RXBUF_SIG)) {
735815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n",
735915516c77SSepherosa Ziehau 		    pkt->cp_rxbuf_id);
736015516c77SSepherosa Ziehau 		return;
736115516c77SSepherosa Ziehau 	}
736215516c77SSepherosa Ziehau 
736315516c77SSepherosa Ziehau 	count = pkt->cp_rxbuf_cnt;
736415516c77SSepherosa Ziehau 	if (__predict_false(hlen <
736515516c77SSepherosa Ziehau 	    __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) {
736615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count);
736715516c77SSepherosa Ziehau 		return;
736815516c77SSepherosa Ziehau 	}
736915516c77SSepherosa Ziehau 
737015516c77SSepherosa Ziehau 	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
737115516c77SSepherosa Ziehau 	for (i = 0; i < count; ++i) {
737215516c77SSepherosa Ziehau 		int ofs, len;
737315516c77SSepherosa Ziehau 
737415516c77SSepherosa Ziehau 		ofs = pkt->cp_rxbuf[i].rb_ofs;
737515516c77SSepherosa Ziehau 		len = pkt->cp_rxbuf[i].rb_len;
737615516c77SSepherosa Ziehau 		if (__predict_false(ofs + len > HN_RXBUF_SIZE)) {
737715516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, "
737815516c77SSepherosa Ziehau 			    "ofs %d, len %d\n", i, ofs, len);
737915516c77SSepherosa Ziehau 			continue;
738015516c77SSepherosa Ziehau 		}
738115516c77SSepherosa Ziehau 		hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len);
738215516c77SSepherosa Ziehau 	}
738315516c77SSepherosa Ziehau 
738415516c77SSepherosa Ziehau 	/*
738515516c77SSepherosa Ziehau 	 * Ack the consumed RXBUF associated w/ this channel packet,
738615516c77SSepherosa Ziehau 	 * so that this RXBUF can be recycled by the hypervisor.
738715516c77SSepherosa Ziehau 	 */
738815516c77SSepherosa Ziehau 	hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid);
738915516c77SSepherosa Ziehau }
739015516c77SSepherosa Ziehau 
739115516c77SSepherosa Ziehau static void
739215516c77SSepherosa Ziehau hn_nvs_ack_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
739315516c77SSepherosa Ziehau     uint64_t tid)
739415516c77SSepherosa Ziehau {
739515516c77SSepherosa Ziehau 	struct hn_nvs_rndis_ack ack;
739615516c77SSepherosa Ziehau 	int retries, error;
739715516c77SSepherosa Ziehau 
739815516c77SSepherosa Ziehau 	ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK;
739915516c77SSepherosa Ziehau 	ack.nvs_status = HN_NVS_STATUS_OK;
740015516c77SSepherosa Ziehau 
740115516c77SSepherosa Ziehau 	retries = 0;
740215516c77SSepherosa Ziehau again:
740315516c77SSepherosa Ziehau 	error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
740415516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid);
740515516c77SSepherosa Ziehau 	if (__predict_false(error == EAGAIN)) {
740615516c77SSepherosa Ziehau 		/*
740715516c77SSepherosa Ziehau 		 * NOTE:
740815516c77SSepherosa Ziehau 		 * This should _not_ happen in real world, since the
740915516c77SSepherosa Ziehau 		 * consumption of the TX bufring from the TX path is
741015516c77SSepherosa Ziehau 		 * controlled.
741115516c77SSepherosa Ziehau 		 */
741215516c77SSepherosa Ziehau 		if (rxr->hn_ack_failed == 0)
741315516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "RXBUF ack retry\n");
741415516c77SSepherosa Ziehau 		rxr->hn_ack_failed++;
741515516c77SSepherosa Ziehau 		retries++;
741615516c77SSepherosa Ziehau 		if (retries < 10) {
741715516c77SSepherosa Ziehau 			DELAY(100);
741815516c77SSepherosa Ziehau 			goto again;
741915516c77SSepherosa Ziehau 		}
742015516c77SSepherosa Ziehau 		/* RXBUF leaks! */
742115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "RXBUF ack failed\n");
742215516c77SSepherosa Ziehau 	}
742315516c77SSepherosa Ziehau }
742415516c77SSepherosa Ziehau 
742515516c77SSepherosa Ziehau static void
742615516c77SSepherosa Ziehau hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
742715516c77SSepherosa Ziehau {
742815516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr = xrxr;
742915516c77SSepherosa Ziehau 	struct hn_softc *sc = rxr->hn_ifp->if_softc;
743015516c77SSepherosa Ziehau 
743115516c77SSepherosa Ziehau 	for (;;) {
743215516c77SSepherosa Ziehau 		struct vmbus_chanpkt_hdr *pkt = rxr->hn_pktbuf;
743315516c77SSepherosa Ziehau 		int error, pktlen;
743415516c77SSepherosa Ziehau 
743515516c77SSepherosa Ziehau 		pktlen = rxr->hn_pktbuf_len;
743615516c77SSepherosa Ziehau 		error = vmbus_chan_recv_pkt(chan, pkt, &pktlen);
743715516c77SSepherosa Ziehau 		if (__predict_false(error == ENOBUFS)) {
743815516c77SSepherosa Ziehau 			void *nbuf;
743915516c77SSepherosa Ziehau 			int nlen;
744015516c77SSepherosa Ziehau 
744115516c77SSepherosa Ziehau 			/*
744215516c77SSepherosa Ziehau 			 * Expand channel packet buffer.
744315516c77SSepherosa Ziehau 			 *
744415516c77SSepherosa Ziehau 			 * XXX
744515516c77SSepherosa Ziehau 			 * Use M_WAITOK here, since allocation failure
744615516c77SSepherosa Ziehau 			 * is fatal.
744715516c77SSepherosa Ziehau 			 */
744815516c77SSepherosa Ziehau 			nlen = rxr->hn_pktbuf_len * 2;
744915516c77SSepherosa Ziehau 			while (nlen < pktlen)
745015516c77SSepherosa Ziehau 				nlen *= 2;
745115516c77SSepherosa Ziehau 			nbuf = malloc(nlen, M_DEVBUF, M_WAITOK);
745215516c77SSepherosa Ziehau 
745315516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "expand pktbuf %d -> %d\n",
745415516c77SSepherosa Ziehau 			    rxr->hn_pktbuf_len, nlen);
745515516c77SSepherosa Ziehau 
745615516c77SSepherosa Ziehau 			free(rxr->hn_pktbuf, M_DEVBUF);
745715516c77SSepherosa Ziehau 			rxr->hn_pktbuf = nbuf;
745815516c77SSepherosa Ziehau 			rxr->hn_pktbuf_len = nlen;
745915516c77SSepherosa Ziehau 			/* Retry! */
746015516c77SSepherosa Ziehau 			continue;
746115516c77SSepherosa Ziehau 		} else if (__predict_false(error == EAGAIN)) {
746215516c77SSepherosa Ziehau 			/* No more channel packets; done! */
746315516c77SSepherosa Ziehau 			break;
746415516c77SSepherosa Ziehau 		}
746515516c77SSepherosa Ziehau 		KASSERT(!error, ("vmbus_chan_recv_pkt failed: %d", error));
746615516c77SSepherosa Ziehau 
746715516c77SSepherosa Ziehau 		switch (pkt->cph_type) {
746815516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_COMP:
746915516c77SSepherosa Ziehau 			hn_nvs_handle_comp(sc, chan, pkt);
747015516c77SSepherosa Ziehau 			break;
747115516c77SSepherosa Ziehau 
747215516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_RXBUF:
747315516c77SSepherosa Ziehau 			hn_nvs_handle_rxbuf(rxr, chan, pkt);
747415516c77SSepherosa Ziehau 			break;
747515516c77SSepherosa Ziehau 
747615516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_INBAND:
747715516c77SSepherosa Ziehau 			hn_nvs_handle_notify(sc, pkt);
747815516c77SSepherosa Ziehau 			break;
747915516c77SSepherosa Ziehau 
748015516c77SSepherosa Ziehau 		default:
748115516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "unknown chan pkt %u\n",
748215516c77SSepherosa Ziehau 			    pkt->cph_type);
748315516c77SSepherosa Ziehau 			break;
748415516c77SSepherosa Ziehau 		}
748515516c77SSepherosa Ziehau 	}
748615516c77SSepherosa Ziehau 	hn_chan_rollup(rxr, rxr->hn_txr);
748715516c77SSepherosa Ziehau }
748815516c77SSepherosa Ziehau 
748915516c77SSepherosa Ziehau static void
7490499c3e17SSepherosa Ziehau hn_sysinit(void *arg __unused)
749115516c77SSepherosa Ziehau {
7492fdd0222aSSepherosa Ziehau 	int i;
7493fdd0222aSSepherosa Ziehau 
74942be266caSSepherosa Ziehau 	hn_udpcs_fixup = counter_u64_alloc(M_WAITOK);
74952be266caSSepherosa Ziehau 
74969c6cae24SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
74979c6cae24SSepherosa Ziehau 	/*
74989c6cae24SSepherosa Ziehau 	 * Don't use ifnet.if_start if transparent VF mode is requested;
74999c6cae24SSepherosa Ziehau 	 * mainly due to the IFF_DRV_OACTIVE flag.
75009c6cae24SSepherosa Ziehau 	 */
75019c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_use_if_start) {
75029c6cae24SSepherosa Ziehau 		hn_use_if_start = 0;
75039c6cae24SSepherosa Ziehau 		printf("hn: tranparent VF mode, if_transmit will be used, "
75049c6cae24SSepherosa Ziehau 		    "instead of if_start\n");
75059c6cae24SSepherosa Ziehau 	}
75069c6cae24SSepherosa Ziehau #endif
75079c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_attwait < HN_XPNT_VF_ATTWAIT_MIN) {
75089c6cae24SSepherosa Ziehau 		printf("hn: invalid transparent VF attach routing "
75099c6cae24SSepherosa Ziehau 		    "wait timeout %d, reset to %d\n",
75109c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_attwait, HN_XPNT_VF_ATTWAIT_MIN);
75119c6cae24SSepherosa Ziehau 		hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
75129c6cae24SSepherosa Ziehau 	}
75139c6cae24SSepherosa Ziehau 
7514fdd0222aSSepherosa Ziehau 	/*
7515499c3e17SSepherosa Ziehau 	 * Initialize VF map.
7516499c3e17SSepherosa Ziehau 	 */
7517499c3e17SSepherosa Ziehau 	rm_init_flags(&hn_vfmap_lock, "hn_vfmap", RM_SLEEPABLE);
7518499c3e17SSepherosa Ziehau 	hn_vfmap_size = HN_VFMAP_SIZE_DEF;
7519499c3e17SSepherosa Ziehau 	hn_vfmap = malloc(sizeof(struct ifnet *) * hn_vfmap_size, M_DEVBUF,
7520499c3e17SSepherosa Ziehau 	    M_WAITOK | M_ZERO);
7521499c3e17SSepherosa Ziehau 
7522499c3e17SSepherosa Ziehau 	/*
7523fdd0222aSSepherosa Ziehau 	 * Fix the # of TX taskqueues.
7524fdd0222aSSepherosa Ziehau 	 */
7525fdd0222aSSepherosa Ziehau 	if (hn_tx_taskq_cnt <= 0)
7526fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = 1;
7527fdd0222aSSepherosa Ziehau 	else if (hn_tx_taskq_cnt > mp_ncpus)
7528fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = mp_ncpus;
752915516c77SSepherosa Ziehau 
75300e11868dSSepherosa Ziehau 	/*
75310e11868dSSepherosa Ziehau 	 * Fix the TX taskqueue mode.
75320e11868dSSepherosa Ziehau 	 */
75330e11868dSSepherosa Ziehau 	switch (hn_tx_taskq_mode) {
75340e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_INDEP:
75350e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_GLOBAL:
75360e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_EVTTQ:
75370e11868dSSepherosa Ziehau 		break;
75380e11868dSSepherosa Ziehau 	default:
75390e11868dSSepherosa Ziehau 		hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
75400e11868dSSepherosa Ziehau 		break;
75410e11868dSSepherosa Ziehau 	}
75420e11868dSSepherosa Ziehau 
754315516c77SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV)
754415516c77SSepherosa Ziehau 		return;
754515516c77SSepherosa Ziehau 
75460e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode != HN_TX_TASKQ_M_GLOBAL)
754715516c77SSepherosa Ziehau 		return;
754815516c77SSepherosa Ziehau 
7549fdd0222aSSepherosa Ziehau 	hn_tx_taskque = malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
7550fdd0222aSSepherosa Ziehau 	    M_DEVBUF, M_WAITOK);
7551fdd0222aSSepherosa Ziehau 	for (i = 0; i < hn_tx_taskq_cnt; ++i) {
7552fdd0222aSSepherosa Ziehau 		hn_tx_taskque[i] = taskqueue_create("hn_tx", M_WAITOK,
7553fdd0222aSSepherosa Ziehau 		    taskqueue_thread_enqueue, &hn_tx_taskque[i]);
7554fdd0222aSSepherosa Ziehau 		taskqueue_start_threads(&hn_tx_taskque[i], 1, PI_NET,
7555fdd0222aSSepherosa Ziehau 		    "hn tx%d", i);
7556fdd0222aSSepherosa Ziehau 	}
755715516c77SSepherosa Ziehau }
7558499c3e17SSepherosa Ziehau SYSINIT(hn_sysinit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysinit, NULL);
755915516c77SSepherosa Ziehau 
756015516c77SSepherosa Ziehau static void
7561499c3e17SSepherosa Ziehau hn_sysuninit(void *arg __unused)
756215516c77SSepherosa Ziehau {
756315516c77SSepherosa Ziehau 
7564fdd0222aSSepherosa Ziehau 	if (hn_tx_taskque != NULL) {
7565fdd0222aSSepherosa Ziehau 		int i;
7566fdd0222aSSepherosa Ziehau 
7567fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
7568fdd0222aSSepherosa Ziehau 			taskqueue_free(hn_tx_taskque[i]);
7569fdd0222aSSepherosa Ziehau 		free(hn_tx_taskque, M_DEVBUF);
7570fdd0222aSSepherosa Ziehau 	}
7571499c3e17SSepherosa Ziehau 
7572499c3e17SSepherosa Ziehau 	if (hn_vfmap != NULL)
7573499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
7574499c3e17SSepherosa Ziehau 	rm_destroy(&hn_vfmap_lock);
75752be266caSSepherosa Ziehau 
75762be266caSSepherosa Ziehau 	counter_u64_free(hn_udpcs_fixup);
757715516c77SSepherosa Ziehau }
7578499c3e17SSepherosa Ziehau SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL);
7579