xref: /freebsd/sys/dev/hyperv/netvsc/if_hn.c (revision 62f9bcf2b4271d58ccf0bd8a81c540bb99a53ef7)
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>
74b3460f44SWei Hu #include <sys/proc.h>
75499c3e17SSepherosa Ziehau #include <sys/rmlock.h>
76499c3e17SSepherosa Ziehau #include <sys/sbuf.h>
77b3460f44SWei Hu #include <sys/sched.h>
7815516c77SSepherosa Ziehau #include <sys/smp.h>
7915516c77SSepherosa Ziehau #include <sys/socket.h>
8015516c77SSepherosa Ziehau #include <sys/sockio.h>
8115516c77SSepherosa Ziehau #include <sys/sx.h>
8215516c77SSepherosa Ziehau #include <sys/sysctl.h>
8315516c77SSepherosa Ziehau #include <sys/taskqueue.h>
8415516c77SSepherosa Ziehau #include <sys/buf_ring.h>
855bdfd3fdSDexuan Cui #include <sys/eventhandler.h>
8626d79d40SMichael Tuexen #include <sys/epoch.h>
8715516c77SSepherosa Ziehau 
88*62f9bcf2SAndrew Turner #include <vm/vm.h>
89*62f9bcf2SAndrew Turner #include <vm/vm_extern.h>
90*62f9bcf2SAndrew Turner #include <vm/pmap.h>
91*62f9bcf2SAndrew Turner 
9215516c77SSepherosa Ziehau #include <machine/atomic.h>
9315516c77SSepherosa Ziehau #include <machine/in_cksum.h>
9415516c77SSepherosa Ziehau 
9515516c77SSepherosa Ziehau #include <net/bpf.h>
9615516c77SSepherosa Ziehau #include <net/ethernet.h>
9715516c77SSepherosa Ziehau #include <net/if.h>
985bdfd3fdSDexuan Cui #include <net/if_dl.h>
9915516c77SSepherosa Ziehau #include <net/if_media.h>
10015516c77SSepherosa Ziehau #include <net/if_types.h>
10115516c77SSepherosa Ziehau #include <net/if_var.h>
10215516c77SSepherosa Ziehau #include <net/rndis.h>
10334d68912SSepherosa Ziehau #ifdef RSS
10434d68912SSepherosa Ziehau #include <net/rss_config.h>
10534d68912SSepherosa Ziehau #endif
10615516c77SSepherosa Ziehau 
10715516c77SSepherosa Ziehau #include <netinet/in_systm.h>
10815516c77SSepherosa Ziehau #include <netinet/in.h>
10915516c77SSepherosa Ziehau #include <netinet/ip.h>
11015516c77SSepherosa Ziehau #include <netinet/ip6.h>
11115516c77SSepherosa Ziehau #include <netinet/tcp.h>
11215516c77SSepherosa Ziehau #include <netinet/tcp_lro.h>
11315516c77SSepherosa Ziehau #include <netinet/udp.h>
11415516c77SSepherosa Ziehau 
11515516c77SSepherosa Ziehau #include <dev/hyperv/include/hyperv.h>
11615516c77SSepherosa Ziehau #include <dev/hyperv/include/hyperv_busdma.h>
11715516c77SSepherosa Ziehau #include <dev/hyperv/include/vmbus.h>
11815516c77SSepherosa Ziehau #include <dev/hyperv/include/vmbus_xact.h>
11915516c77SSepherosa Ziehau 
12015516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/ndis.h>
12115516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/if_hnreg.h>
12215516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/if_hnvar.h>
12315516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/hn_nvs.h>
12415516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/hn_rndis.h>
12515516c77SSepherosa Ziehau 
12615516c77SSepherosa Ziehau #include "vmbus_if.h"
12715516c77SSepherosa Ziehau 
12823bf9e15SSepherosa Ziehau #define HN_IFSTART_SUPPORT
12923bf9e15SSepherosa Ziehau 
13015516c77SSepherosa Ziehau #define HN_RING_CNT_DEF_MAX		8
13115516c77SSepherosa Ziehau 
132499c3e17SSepherosa Ziehau #define HN_VFMAP_SIZE_DEF		8
133499c3e17SSepherosa Ziehau 
1349c6cae24SSepherosa Ziehau #define HN_XPNT_VF_ATTWAIT_MIN		2	/* seconds */
1359c6cae24SSepherosa Ziehau 
13615516c77SSepherosa Ziehau /* YYY should get it from the underlying channel */
13715516c77SSepherosa Ziehau #define HN_TX_DESC_CNT			512
13815516c77SSepherosa Ziehau 
13915516c77SSepherosa Ziehau #define HN_RNDIS_PKT_LEN					\
14015516c77SSepherosa Ziehau 	(sizeof(struct rndis_packet_msg) +			\
14115516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(HN_NDIS_HASH_VALUE_SIZE) +	\
14215516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_VLAN_INFO_SIZE) +		\
14315516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_LSO2_INFO_SIZE) +		\
14415516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_TXCSUM_INFO_SIZE))
14515516c77SSepherosa Ziehau #define HN_RNDIS_PKT_BOUNDARY		PAGE_SIZE
14615516c77SSepherosa Ziehau #define HN_RNDIS_PKT_ALIGN		CACHE_LINE_SIZE
14715516c77SSepherosa Ziehau 
14815516c77SSepherosa Ziehau #define HN_TX_DATA_BOUNDARY		PAGE_SIZE
14915516c77SSepherosa Ziehau #define HN_TX_DATA_MAXSIZE		IP_MAXPACKET
15015516c77SSepherosa Ziehau #define HN_TX_DATA_SEGSIZE		PAGE_SIZE
15115516c77SSepherosa Ziehau /* -1 for RNDIS packet message */
15215516c77SSepherosa Ziehau #define HN_TX_DATA_SEGCNT_MAX		(HN_GPACNT_MAX - 1)
15315516c77SSepherosa Ziehau 
15415516c77SSepherosa Ziehau #define HN_DIRECT_TX_SIZE_DEF		128
15515516c77SSepherosa Ziehau 
15615516c77SSepherosa Ziehau #define HN_EARLY_TXEOF_THRESH		8
15715516c77SSepherosa Ziehau 
15815516c77SSepherosa Ziehau #define HN_PKTBUF_LEN_DEF		(16 * 1024)
15915516c77SSepherosa Ziehau 
16015516c77SSepherosa Ziehau #define HN_LROENT_CNT_DEF		128
16115516c77SSepherosa Ziehau 
16215516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MULTIRX_DEF	(12 * ETHERMTU)
16315516c77SSepherosa Ziehau #define HN_LRO_LENLIM_DEF		(25 * ETHERMTU)
16415516c77SSepherosa Ziehau /* YYY 2*MTU is a bit rough, but should be good enough. */
1654db5958aSJustin Hibbits #define HN_LRO_LENLIM_MIN(ifp)		(2 * if_getmtu(ifp))
16615516c77SSepherosa Ziehau 
16715516c77SSepherosa Ziehau #define HN_LRO_ACKCNT_DEF		1
16815516c77SSepherosa Ziehau 
16915516c77SSepherosa Ziehau #define HN_LOCK_INIT(sc)		\
17015516c77SSepherosa Ziehau 	sx_init(&(sc)->hn_lock, device_get_nameunit((sc)->hn_dev))
17115516c77SSepherosa Ziehau #define HN_LOCK_DESTROY(sc)		sx_destroy(&(sc)->hn_lock)
17215516c77SSepherosa Ziehau #define HN_LOCK_ASSERT(sc)		sx_assert(&(sc)->hn_lock, SA_XLOCKED)
173fdc4f478SSepherosa Ziehau #define HN_LOCK(sc)					\
174fdc4f478SSepherosa Ziehau do {							\
175b3460f44SWei Hu 	while (sx_try_xlock(&(sc)->hn_lock) == 0) {	\
176b3460f44SWei Hu 		/* Relinquish cpu to avoid deadlock */	\
177b3460f44SWei Hu 		sched_relinquish(curthread);		\
178fdc4f478SSepherosa Ziehau 		DELAY(1000);				\
179b3460f44SWei Hu 	}						\
180fdc4f478SSepherosa Ziehau } while (0)
18115516c77SSepherosa Ziehau #define HN_UNLOCK(sc)			sx_xunlock(&(sc)->hn_lock)
18215516c77SSepherosa Ziehau 
18315516c77SSepherosa Ziehau #define HN_CSUM_IP_MASK			(CSUM_IP | CSUM_IP_TCP | CSUM_IP_UDP)
18415516c77SSepherosa Ziehau #define HN_CSUM_IP6_MASK		(CSUM_IP6_TCP | CSUM_IP6_UDP)
18515516c77SSepherosa Ziehau #define HN_CSUM_IP_HWASSIST(sc)		\
18615516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP_MASK)
18715516c77SSepherosa Ziehau #define HN_CSUM_IP6_HWASSIST(sc)	\
18815516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP6_MASK)
18915516c77SSepherosa Ziehau 
190dc13fee6SSepherosa Ziehau #define HN_PKTSIZE_MIN(align)		\
191dc13fee6SSepherosa Ziehau 	roundup2(ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN - ETHER_CRC_LEN + \
192dc13fee6SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN, (align))
193dc13fee6SSepherosa Ziehau #define HN_PKTSIZE(m, align)		\
194dc13fee6SSepherosa Ziehau 	roundup2((m)->m_pkthdr.len + HN_RNDIS_PKT_LEN, (align))
195dc13fee6SSepherosa Ziehau 
19634d68912SSepherosa Ziehau #ifdef RSS
19734d68912SSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	rss_getcpu((idx) % rss_getnumbuckets())
19834d68912SSepherosa Ziehau #else
1990e11868dSSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	(((sc)->hn_cpu + (idx)) % mp_ncpus)
20034d68912SSepherosa Ziehau #endif
2010e11868dSSepherosa Ziehau 
20215516c77SSepherosa Ziehau struct hn_txdesc {
20315516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
20415516c77SSepherosa Ziehau 	SLIST_ENTRY(hn_txdesc)		link;
20515516c77SSepherosa Ziehau #endif
206dc13fee6SSepherosa Ziehau 	STAILQ_ENTRY(hn_txdesc)		agg_link;
207dc13fee6SSepherosa Ziehau 
208dc13fee6SSepherosa Ziehau 	/* Aggregated txdescs, in sending order. */
209dc13fee6SSepherosa Ziehau 	STAILQ_HEAD(, hn_txdesc)	agg_list;
210dc13fee6SSepherosa Ziehau 
211dc13fee6SSepherosa Ziehau 	/* The oldest packet, if transmission aggregation happens. */
21215516c77SSepherosa Ziehau 	struct mbuf			*m;
21315516c77SSepherosa Ziehau 	struct hn_tx_ring		*txr;
21415516c77SSepherosa Ziehau 	int				refs;
21515516c77SSepherosa Ziehau 	uint32_t			flags;	/* HN_TXD_FLAG_ */
21615516c77SSepherosa Ziehau 	struct hn_nvs_sendctx		send_ctx;
21715516c77SSepherosa Ziehau 	uint32_t			chim_index;
21815516c77SSepherosa Ziehau 	int				chim_size;
21915516c77SSepherosa Ziehau 
22015516c77SSepherosa Ziehau 	bus_dmamap_t			data_dmap;
22115516c77SSepherosa Ziehau 
22215516c77SSepherosa Ziehau 	bus_addr_t			rndis_pkt_paddr;
22315516c77SSepherosa Ziehau 	struct rndis_packet_msg		*rndis_pkt;
22415516c77SSepherosa Ziehau 	bus_dmamap_t			rndis_pkt_dmap;
22515516c77SSepherosa Ziehau };
22615516c77SSepherosa Ziehau 
22715516c77SSepherosa Ziehau #define HN_TXD_FLAG_ONLIST		0x0001
22815516c77SSepherosa Ziehau #define HN_TXD_FLAG_DMAMAP		0x0002
229dc13fee6SSepherosa Ziehau #define HN_TXD_FLAG_ONAGG		0x0004
23015516c77SSepherosa Ziehau 
231a491581fSWei Hu #define	HN_NDIS_PKTINFO_SUBALLOC	0x01
232a491581fSWei Hu #define	HN_NDIS_PKTINFO_1ST_FRAG	0x02
233a491581fSWei Hu #define	HN_NDIS_PKTINFO_LAST_FRAG	0x04
234a491581fSWei Hu 
235a491581fSWei Hu struct packet_info_id {
236a491581fSWei Hu 	uint8_t				ver;
237a491581fSWei Hu 	uint8_t				flag;
238a491581fSWei Hu 	uint16_t			pkt_id;
239a491581fSWei Hu };
240a491581fSWei Hu 
241a491581fSWei Hu #define NDIS_PKTINFOID_SZ		sizeof(struct packet_info_id)
242a491581fSWei Hu 
243a491581fSWei Hu 
24415516c77SSepherosa Ziehau struct hn_rxinfo {
245a491581fSWei Hu 	const uint32_t			*vlan_info;
246a491581fSWei Hu 	const uint32_t			*csum_info;
247a491581fSWei Hu 	const uint32_t			*hash_info;
248a491581fSWei Hu 	const uint32_t			*hash_value;
249a491581fSWei Hu 	const struct packet_info_id	*pktinfo_id;
25015516c77SSepherosa Ziehau };
25115516c77SSepherosa Ziehau 
252962f0357SSepherosa Ziehau struct hn_rxvf_setarg {
2535bdfd3fdSDexuan Cui 	struct hn_rx_ring	*rxr;
2544db5958aSJustin Hibbits 	if_t			vf_ifp;
2555bdfd3fdSDexuan Cui };
2565bdfd3fdSDexuan Cui 
25715516c77SSepherosa Ziehau #define HN_RXINFO_VLAN			0x0001
25815516c77SSepherosa Ziehau #define HN_RXINFO_CSUM			0x0002
25915516c77SSepherosa Ziehau #define HN_RXINFO_HASHINF		0x0004
26015516c77SSepherosa Ziehau #define HN_RXINFO_HASHVAL		0x0008
261a491581fSWei Hu #define HN_RXINFO_PKTINFO_ID		0x0010
26215516c77SSepherosa Ziehau #define HN_RXINFO_ALL			\
26315516c77SSepherosa Ziehau 	(HN_RXINFO_VLAN |		\
26415516c77SSepherosa Ziehau 	 HN_RXINFO_CSUM |		\
26515516c77SSepherosa Ziehau 	 HN_RXINFO_HASHINF |		\
266a491581fSWei Hu 	 HN_RXINFO_HASHVAL |		\
267a491581fSWei Hu 	 HN_RXINFO_PKTINFO_ID)
26815516c77SSepherosa Ziehau 
26915516c77SSepherosa Ziehau static int			hn_probe(device_t);
27015516c77SSepherosa Ziehau static int			hn_attach(device_t);
27115516c77SSepherosa Ziehau static int			hn_detach(device_t);
27215516c77SSepherosa Ziehau static int			hn_shutdown(device_t);
27315516c77SSepherosa Ziehau static void			hn_chan_callback(struct vmbus_channel *,
27415516c77SSepherosa Ziehau 				    void *);
27515516c77SSepherosa Ziehau 
27615516c77SSepherosa Ziehau static void			hn_init(void *);
2774db5958aSJustin Hibbits static int			hn_ioctl(if_t, u_long, caddr_t);
27823bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
2794db5958aSJustin Hibbits static void			hn_start(if_t);
28023bf9e15SSepherosa Ziehau #endif
2814db5958aSJustin Hibbits static int			hn_transmit(if_t, struct mbuf *);
2824db5958aSJustin Hibbits static void			hn_xmit_qflush(if_t);
2834db5958aSJustin Hibbits static int			hn_ifmedia_upd(if_t);
2844db5958aSJustin Hibbits static void			hn_ifmedia_sts(if_t,
28515516c77SSepherosa Ziehau 				    struct ifmediareq *);
28615516c77SSepherosa Ziehau 
2874db5958aSJustin Hibbits static void			hn_ifnet_event(void *, if_t, int);
2884db5958aSJustin Hibbits static void			hn_ifaddr_event(void *, if_t);
2894db5958aSJustin Hibbits static void			hn_ifnet_attevent(void *, if_t);
2904db5958aSJustin Hibbits static void			hn_ifnet_detevent(void *, if_t);
2914db5958aSJustin Hibbits static void			hn_ifnet_lnkevent(void *, if_t, int);
292499c3e17SSepherosa Ziehau 
293962f0357SSepherosa Ziehau static bool			hn_ismyvf(const struct hn_softc *,
2944db5958aSJustin Hibbits 				    const if_t);
295962f0357SSepherosa Ziehau static void			hn_rxvf_change(struct hn_softc *,
2964db5958aSJustin Hibbits 				    if_t, bool);
2974db5958aSJustin Hibbits static void			hn_rxvf_set(struct hn_softc *, if_t);
298962f0357SSepherosa Ziehau static void			hn_rxvf_set_task(void *, int);
2994db5958aSJustin Hibbits static void			hn_xpnt_vf_input(if_t, struct mbuf *);
3009c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_iocsetflags(struct hn_softc *);
3019c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_iocsetcaps(struct hn_softc *,
3029c6cae24SSepherosa Ziehau 				    struct ifreq *);
3039c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_saveifflags(struct hn_softc *);
3049c6cae24SSepherosa Ziehau static bool			hn_xpnt_vf_isready(struct hn_softc *);
3059c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_setready(struct hn_softc *);
3069c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_init_taskfunc(void *, int);
3079c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_init(struct hn_softc *);
308a97fff19SSepherosa Ziehau static void			hn_xpnt_vf_setenable(struct hn_softc *);
309a97fff19SSepherosa Ziehau static void			hn_xpnt_vf_setdisable(struct hn_softc *, bool);
310642ec226SSepherosa Ziehau static void			hn_vf_rss_fixup(struct hn_softc *, bool);
311642ec226SSepherosa Ziehau static void			hn_vf_rss_restore(struct hn_softc *);
312962f0357SSepherosa Ziehau 
31315516c77SSepherosa Ziehau static int			hn_rndis_rxinfo(const void *, int,
31415516c77SSepherosa Ziehau 				    struct hn_rxinfo *);
31515516c77SSepherosa Ziehau static void			hn_rndis_rx_data(struct hn_rx_ring *,
31615516c77SSepherosa Ziehau 				    const void *, int);
31715516c77SSepherosa Ziehau static void			hn_rndis_rx_status(struct hn_softc *,
31815516c77SSepherosa Ziehau 				    const void *, int);
319b3b75d9cSSepherosa Ziehau static void			hn_rndis_init_fixat(struct hn_softc *, int);
32015516c77SSepherosa Ziehau 
32115516c77SSepherosa Ziehau static void			hn_nvs_handle_notify(struct hn_softc *,
32215516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
32315516c77SSepherosa Ziehau static void			hn_nvs_handle_comp(struct hn_softc *,
32415516c77SSepherosa Ziehau 				    struct vmbus_channel *,
32515516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
32615516c77SSepherosa Ziehau static void			hn_nvs_handle_rxbuf(struct hn_rx_ring *,
32715516c77SSepherosa Ziehau 				    struct vmbus_channel *,
32815516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
32915516c77SSepherosa Ziehau static void			hn_nvs_ack_rxbuf(struct hn_rx_ring *,
33015516c77SSepherosa Ziehau 				    struct vmbus_channel *, uint64_t);
33115516c77SSepherosa Ziehau 
33215516c77SSepherosa Ziehau static int			hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS);
33315516c77SSepherosa Ziehau static int			hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS);
33415516c77SSepherosa Ziehau static int			hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS);
33515516c77SSepherosa Ziehau static int			hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS);
33615516c77SSepherosa Ziehau static int			hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS);
33715516c77SSepherosa Ziehau static int			hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
33815516c77SSepherosa Ziehau static int			hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
33915516c77SSepherosa Ziehau static int			hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS);
34015516c77SSepherosa Ziehau static int			hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS);
34115516c77SSepherosa Ziehau static int			hn_caps_sysctl(SYSCTL_HANDLER_ARGS);
34215516c77SSepherosa Ziehau static int			hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS);
34315516c77SSepherosa Ziehau static int			hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS);
34434d68912SSepherosa Ziehau #ifndef RSS
34515516c77SSepherosa Ziehau static int			hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS);
34615516c77SSepherosa Ziehau static int			hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS);
34734d68912SSepherosa Ziehau #endif
34815516c77SSepherosa Ziehau static int			hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS);
349642ec226SSepherosa Ziehau static int			hn_rss_hcap_sysctl(SYSCTL_HANDLER_ARGS);
350642ec226SSepherosa Ziehau static int			hn_rss_mbuf_sysctl(SYSCTL_HANDLER_ARGS);
351dc13fee6SSepherosa Ziehau static int			hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS);
352dc13fee6SSepherosa Ziehau static int			hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS);
353dc13fee6SSepherosa Ziehau static int			hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS);
354dc13fee6SSepherosa Ziehau static int			hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
3556c1204dfSSepherosa Ziehau static int			hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
35640d60d6eSDexuan Cui static int			hn_vf_sysctl(SYSCTL_HANDLER_ARGS);
357499c3e17SSepherosa Ziehau static int			hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS);
358499c3e17SSepherosa Ziehau static int			hn_vflist_sysctl(SYSCTL_HANDLER_ARGS);
359499c3e17SSepherosa Ziehau static int			hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS);
3609c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS);
3619c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS);
36215516c77SSepherosa Ziehau 
3635bdfd3fdSDexuan Cui static void			hn_stop(struct hn_softc *, bool);
36415516c77SSepherosa Ziehau static void			hn_init_locked(struct hn_softc *);
36515516c77SSepherosa Ziehau static int			hn_chan_attach(struct hn_softc *,
36615516c77SSepherosa Ziehau 				    struct vmbus_channel *);
36715516c77SSepherosa Ziehau static void			hn_chan_detach(struct hn_softc *,
36815516c77SSepherosa Ziehau 				    struct vmbus_channel *);
36915516c77SSepherosa Ziehau static int			hn_attach_subchans(struct hn_softc *);
37015516c77SSepherosa Ziehau static void			hn_detach_allchans(struct hn_softc *);
37115516c77SSepherosa Ziehau static void			hn_chan_rollup(struct hn_rx_ring *,
37215516c77SSepherosa Ziehau 				    struct hn_tx_ring *);
37315516c77SSepherosa Ziehau static void			hn_set_ring_inuse(struct hn_softc *, int);
37415516c77SSepherosa Ziehau static int			hn_synth_attach(struct hn_softc *, int);
37515516c77SSepherosa Ziehau static void			hn_synth_detach(struct hn_softc *);
37615516c77SSepherosa Ziehau static int			hn_synth_alloc_subchans(struct hn_softc *,
37715516c77SSepherosa Ziehau 				    int *);
3782494d735SSepherosa Ziehau static bool			hn_synth_attachable(const struct hn_softc *);
37915516c77SSepherosa Ziehau static void			hn_suspend(struct hn_softc *);
38015516c77SSepherosa Ziehau static void			hn_suspend_data(struct hn_softc *);
38115516c77SSepherosa Ziehau static void			hn_suspend_mgmt(struct hn_softc *);
38215516c77SSepherosa Ziehau static void			hn_resume(struct hn_softc *);
38315516c77SSepherosa Ziehau static void			hn_resume_data(struct hn_softc *);
38415516c77SSepherosa Ziehau static void			hn_resume_mgmt(struct hn_softc *);
38515516c77SSepherosa Ziehau static void			hn_suspend_mgmt_taskfunc(void *, int);
38625641fc7SSepherosa Ziehau static void			hn_chan_drain(struct hn_softc *,
38725641fc7SSepherosa Ziehau 				    struct vmbus_channel *);
388b3b75d9cSSepherosa Ziehau static void			hn_disable_rx(struct hn_softc *);
389b3b75d9cSSepherosa Ziehau static void			hn_drain_rxtx(struct hn_softc *, int);
3906c1204dfSSepherosa Ziehau static void			hn_polling(struct hn_softc *, u_int);
3916c1204dfSSepherosa Ziehau static void			hn_chan_polling(struct vmbus_channel *, u_int);
3929c6cae24SSepherosa Ziehau static void			hn_mtu_change_fixup(struct hn_softc *);
39315516c77SSepherosa Ziehau 
39415516c77SSepherosa Ziehau static void			hn_update_link_status(struct hn_softc *);
39515516c77SSepherosa Ziehau static void			hn_change_network(struct hn_softc *);
39615516c77SSepherosa Ziehau static void			hn_link_taskfunc(void *, int);
39715516c77SSepherosa Ziehau static void			hn_netchg_init_taskfunc(void *, int);
39815516c77SSepherosa Ziehau static void			hn_netchg_status_taskfunc(void *, int);
39915516c77SSepherosa Ziehau static void			hn_link_status(struct hn_softc *);
40015516c77SSepherosa Ziehau 
40115516c77SSepherosa Ziehau static int			hn_create_rx_data(struct hn_softc *, int);
40215516c77SSepherosa Ziehau static void			hn_destroy_rx_data(struct hn_softc *);
40315516c77SSepherosa Ziehau static int			hn_check_iplen(const struct mbuf *, int);
404db76829bSSepherosa Ziehau static void			hn_rxpkt_proto(const struct mbuf *, int *, int *);
405f1b0a43fSSepherosa Ziehau static int			hn_set_rxfilter(struct hn_softc *, uint32_t);
406c08f7b2cSSepherosa Ziehau static int			hn_rxfilter_config(struct hn_softc *);
40715516c77SSepherosa Ziehau static int			hn_rss_reconfig(struct hn_softc *);
408afd4971bSSepherosa Ziehau static void			hn_rss_ind_fixup(struct hn_softc *);
409642ec226SSepherosa Ziehau static void			hn_rss_mbuf_hash(struct hn_softc *, uint32_t);
410a491581fSWei Hu static int			hn_rxpkt(struct hn_rx_ring *);
411642ec226SSepherosa Ziehau static uint32_t			hn_rss_type_fromndis(uint32_t);
412642ec226SSepherosa Ziehau static uint32_t			hn_rss_type_tondis(uint32_t);
41315516c77SSepherosa Ziehau 
41415516c77SSepherosa Ziehau static int			hn_tx_ring_create(struct hn_softc *, int);
41515516c77SSepherosa Ziehau static void			hn_tx_ring_destroy(struct hn_tx_ring *);
41615516c77SSepherosa Ziehau static int			hn_create_tx_data(struct hn_softc *, int);
41715516c77SSepherosa Ziehau static void			hn_fixup_tx_data(struct hn_softc *);
418db76829bSSepherosa Ziehau static void			hn_fixup_rx_data(struct hn_softc *);
41915516c77SSepherosa Ziehau static void			hn_destroy_tx_data(struct hn_softc *);
42015516c77SSepherosa Ziehau static void			hn_txdesc_dmamap_destroy(struct hn_txdesc *);
42125641fc7SSepherosa Ziehau static void			hn_txdesc_gc(struct hn_tx_ring *,
42225641fc7SSepherosa Ziehau 				    struct hn_txdesc *);
4234db5958aSJustin Hibbits static int			hn_encap(if_t, struct hn_tx_ring *,
42415516c77SSepherosa Ziehau 				    struct hn_txdesc *, struct mbuf **);
4254db5958aSJustin Hibbits static int			hn_txpkt(if_t, struct hn_tx_ring *,
42615516c77SSepherosa Ziehau 				    struct hn_txdesc *);
42715516c77SSepherosa Ziehau static void			hn_set_chim_size(struct hn_softc *, int);
42815516c77SSepherosa Ziehau static void			hn_set_tso_maxsize(struct hn_softc *, int, int);
42915516c77SSepherosa Ziehau static bool			hn_tx_ring_pending(struct hn_tx_ring *);
43015516c77SSepherosa Ziehau static void			hn_tx_ring_qflush(struct hn_tx_ring *);
43115516c77SSepherosa Ziehau static void			hn_resume_tx(struct hn_softc *, int);
432dc13fee6SSepherosa Ziehau static void			hn_set_txagg(struct hn_softc *);
4334db5958aSJustin Hibbits static void			*hn_try_txagg(if_t,
434dc13fee6SSepherosa Ziehau 				    struct hn_tx_ring *, struct hn_txdesc *,
435dc13fee6SSepherosa Ziehau 				    int);
43615516c77SSepherosa Ziehau static int			hn_get_txswq_depth(const struct hn_tx_ring *);
43715516c77SSepherosa Ziehau static void			hn_txpkt_done(struct hn_nvs_sendctx *,
43815516c77SSepherosa Ziehau 				    struct hn_softc *, struct vmbus_channel *,
43915516c77SSepherosa Ziehau 				    const void *, int);
44015516c77SSepherosa Ziehau static int			hn_txpkt_sglist(struct hn_tx_ring *,
44115516c77SSepherosa Ziehau 				    struct hn_txdesc *);
44215516c77SSepherosa Ziehau static int			hn_txpkt_chim(struct hn_tx_ring *,
44315516c77SSepherosa Ziehau 				    struct hn_txdesc *);
44415516c77SSepherosa Ziehau static int			hn_xmit(struct hn_tx_ring *, int);
44515516c77SSepherosa Ziehau static void			hn_xmit_taskfunc(void *, int);
44615516c77SSepherosa Ziehau static void			hn_xmit_txeof(struct hn_tx_ring *);
44715516c77SSepherosa Ziehau static void			hn_xmit_txeof_taskfunc(void *, int);
44823bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
44915516c77SSepherosa Ziehau static int			hn_start_locked(struct hn_tx_ring *, int);
45015516c77SSepherosa Ziehau static void			hn_start_taskfunc(void *, int);
45115516c77SSepherosa Ziehau static void			hn_start_txeof(struct hn_tx_ring *);
45215516c77SSepherosa Ziehau static void			hn_start_txeof_taskfunc(void *, int);
45323bf9e15SSepherosa Ziehau #endif
45415516c77SSepherosa Ziehau 
45580c3eb7bSWei Hu static int			hn_rsc_sysctl(SYSCTL_HANDLER_ARGS);
45680c3eb7bSWei Hu 
45715516c77SSepherosa Ziehau SYSCTL_NODE(_hw, OID_AUTO, hn, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
45815516c77SSepherosa Ziehau     "Hyper-V network interface");
45915516c77SSepherosa Ziehau 
460b15a632cSGordon Bergling /* Trust tcp segment verification on host side. */
46115516c77SSepherosa Ziehau static int			hn_trust_hosttcp = 1;
46215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hosttcp, CTLFLAG_RDTUN,
46315516c77SSepherosa Ziehau     &hn_trust_hosttcp, 0,
464b15a632cSGordon Bergling     "Trust tcp segment verification on host side, "
46515516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
46615516c77SSepherosa Ziehau 
46715516c77SSepherosa Ziehau /* Trust udp datagrams verification on host side. */
46815516c77SSepherosa Ziehau static int			hn_trust_hostudp = 1;
46915516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostudp, CTLFLAG_RDTUN,
47015516c77SSepherosa Ziehau     &hn_trust_hostudp, 0,
47115516c77SSepherosa Ziehau     "Trust udp datagram verification on host side, "
47215516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
47315516c77SSepherosa Ziehau 
47415516c77SSepherosa Ziehau /* Trust ip packets verification on host side. */
47515516c77SSepherosa Ziehau static int			hn_trust_hostip = 1;
47615516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostip, CTLFLAG_RDTUN,
47715516c77SSepherosa Ziehau     &hn_trust_hostip, 0,
47815516c77SSepherosa Ziehau     "Trust ip packet verification on host side, "
47915516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
48015516c77SSepherosa Ziehau 
4812be266caSSepherosa Ziehau /*
4822be266caSSepherosa Ziehau  * Offload UDP/IPv4 checksum.
4832be266caSSepherosa Ziehau  */
4842be266caSSepherosa Ziehau static int			hn_enable_udp4cs = 1;
4852be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp4cs, CTLFLAG_RDTUN,
4862be266caSSepherosa Ziehau     &hn_enable_udp4cs, 0, "Offload UDP/IPv4 checksum");
4872be266caSSepherosa Ziehau 
4882be266caSSepherosa Ziehau /*
4892be266caSSepherosa Ziehau  * Offload UDP/IPv6 checksum.
4902be266caSSepherosa Ziehau  */
4912be266caSSepherosa Ziehau static int			hn_enable_udp6cs = 1;
4922be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp6cs, CTLFLAG_RDTUN,
4932be266caSSepherosa Ziehau     &hn_enable_udp6cs, 0, "Offload UDP/IPv6 checksum");
4942be266caSSepherosa Ziehau 
4952be266caSSepherosa Ziehau /* Stats. */
4962be266caSSepherosa Ziehau static counter_u64_t		hn_udpcs_fixup;
4972be266caSSepherosa Ziehau SYSCTL_COUNTER_U64(_hw_hn, OID_AUTO, udpcs_fixup, CTLFLAG_RW,
4982be266caSSepherosa Ziehau     &hn_udpcs_fixup, "# of UDP checksum fixup");
4992be266caSSepherosa Ziehau 
5002be266caSSepherosa Ziehau /*
5012be266caSSepherosa Ziehau  * See hn_set_hlen().
5022be266caSSepherosa Ziehau  *
5032be266caSSepherosa Ziehau  * This value is for Azure.  For Hyper-V, set this above
5042be266caSSepherosa Ziehau  * 65536 to disable UDP datagram checksum fixup.
5052be266caSSepherosa Ziehau  */
5062be266caSSepherosa Ziehau static int			hn_udpcs_fixup_mtu = 1420;
5072be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, udpcs_fixup_mtu, CTLFLAG_RWTUN,
5082be266caSSepherosa Ziehau     &hn_udpcs_fixup_mtu, 0, "UDP checksum fixup MTU threshold");
5092be266caSSepherosa Ziehau 
51015516c77SSepherosa Ziehau /* Limit TSO burst size */
51115516c77SSepherosa Ziehau static int			hn_tso_maxlen = IP_MAXPACKET;
51215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tso_maxlen, CTLFLAG_RDTUN,
51315516c77SSepherosa Ziehau     &hn_tso_maxlen, 0, "TSO burst limit");
51415516c77SSepherosa Ziehau 
51515516c77SSepherosa Ziehau /* Limit chimney send size */
51615516c77SSepherosa Ziehau static int			hn_tx_chimney_size = 0;
51715516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_chimney_size, CTLFLAG_RDTUN,
51815516c77SSepherosa Ziehau     &hn_tx_chimney_size, 0, "Chimney send packet size limit");
51915516c77SSepherosa Ziehau 
52015516c77SSepherosa Ziehau /* Limit the size of packet for direct transmission */
52115516c77SSepherosa Ziehau static int			hn_direct_tx_size = HN_DIRECT_TX_SIZE_DEF;
52215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, direct_tx_size, CTLFLAG_RDTUN,
52315516c77SSepherosa Ziehau     &hn_direct_tx_size, 0, "Size of the packet for direct transmission");
52415516c77SSepherosa Ziehau 
52515516c77SSepherosa Ziehau /* # of LRO entries per RX ring */
52615516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
52715516c77SSepherosa Ziehau static int			hn_lro_entry_count = HN_LROENT_CNT_DEF;
52815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, lro_entry_count, CTLFLAG_RDTUN,
52915516c77SSepherosa Ziehau     &hn_lro_entry_count, 0, "LRO entry count");
53015516c77SSepherosa Ziehau #endif
53115516c77SSepherosa Ziehau 
532fdd0222aSSepherosa Ziehau static int			hn_tx_taskq_cnt = 1;
533fdd0222aSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_cnt, CTLFLAG_RDTUN,
534fdd0222aSSepherosa Ziehau     &hn_tx_taskq_cnt, 0, "# of TX taskqueues");
535fdd0222aSSepherosa Ziehau 
5360e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_INDEP	0
5370e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_GLOBAL	1
5380e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_EVTTQ	2
5390e11868dSSepherosa Ziehau 
5400e11868dSSepherosa Ziehau static int			hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
5410e11868dSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_mode, CTLFLAG_RDTUN,
5420e11868dSSepherosa Ziehau     &hn_tx_taskq_mode, 0, "TX taskqueue modes: "
5430e11868dSSepherosa Ziehau     "0 - independent, 1 - share global tx taskqs, 2 - share event taskqs");
5440e11868dSSepherosa Ziehau 
54515516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
54615516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 0;
54715516c77SSepherosa Ziehau #else
54815516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 1;
54915516c77SSepherosa Ziehau #endif
55015516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_txdesc_bufring, CTLFLAG_RD,
55115516c77SSepherosa Ziehau     &hn_use_txdesc_bufring, 0, "Use buf_ring for TX descriptors");
55215516c77SSepherosa Ziehau 
55323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
55415516c77SSepherosa Ziehau /* Use ifnet.if_start instead of ifnet.if_transmit */
55515516c77SSepherosa Ziehau static int			hn_use_if_start = 0;
55615516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_if_start, CTLFLAG_RDTUN,
55715516c77SSepherosa Ziehau     &hn_use_if_start, 0, "Use if_start TX method");
55823bf9e15SSepherosa Ziehau #endif
55915516c77SSepherosa Ziehau 
56015516c77SSepherosa Ziehau /* # of channels to use */
56115516c77SSepherosa Ziehau static int			hn_chan_cnt = 0;
56215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, chan_cnt, CTLFLAG_RDTUN,
56315516c77SSepherosa Ziehau     &hn_chan_cnt, 0,
56415516c77SSepherosa Ziehau     "# of channels to use; each channel has one RX ring and one TX ring");
56515516c77SSepherosa Ziehau 
56615516c77SSepherosa Ziehau /* # of transmit rings to use */
56715516c77SSepherosa Ziehau static int			hn_tx_ring_cnt = 0;
56815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_ring_cnt, CTLFLAG_RDTUN,
56915516c77SSepherosa Ziehau     &hn_tx_ring_cnt, 0, "# of TX rings to use");
57015516c77SSepherosa Ziehau 
57115516c77SSepherosa Ziehau /* Software TX ring deptch */
57215516c77SSepherosa Ziehau static int			hn_tx_swq_depth = 0;
57315516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_swq_depth, CTLFLAG_RDTUN,
57415516c77SSepherosa Ziehau     &hn_tx_swq_depth, 0, "Depth of IFQ or BUFRING");
57515516c77SSepherosa Ziehau 
57615516c77SSepherosa Ziehau /* Enable sorted LRO, and the depth of the per-channel mbuf queue */
57715516c77SSepherosa Ziehau static u_int			hn_lro_mbufq_depth = 0;
57815516c77SSepherosa Ziehau SYSCTL_UINT(_hw_hn, OID_AUTO, lro_mbufq_depth, CTLFLAG_RDTUN,
57915516c77SSepherosa Ziehau     &hn_lro_mbufq_depth, 0, "Depth of LRO mbuf queue");
58015516c77SSepherosa Ziehau 
581dc13fee6SSepherosa Ziehau /* Packet transmission aggregation size limit */
582dc13fee6SSepherosa Ziehau static int			hn_tx_agg_size = -1;
583dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_size, CTLFLAG_RDTUN,
584dc13fee6SSepherosa Ziehau     &hn_tx_agg_size, 0, "Packet transmission aggregation size limit");
585dc13fee6SSepherosa Ziehau 
586dc13fee6SSepherosa Ziehau /* Packet transmission aggregation count limit */
587fa915c4dSSepherosa Ziehau static int			hn_tx_agg_pkts = -1;
588dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_pkts, CTLFLAG_RDTUN,
589dc13fee6SSepherosa Ziehau     &hn_tx_agg_pkts, 0, "Packet transmission aggregation packet limit");
590dc13fee6SSepherosa Ziehau 
591499c3e17SSepherosa Ziehau /* VF list */
5927029da5cSPawel Biernacki SYSCTL_PROC(_hw_hn, OID_AUTO, vflist,
5937029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 0, 0,
5947029da5cSPawel Biernacki     hn_vflist_sysctl, "A",
5957029da5cSPawel Biernacki     "VF list");
596499c3e17SSepherosa Ziehau 
597499c3e17SSepherosa Ziehau /* VF mapping */
5987029da5cSPawel Biernacki SYSCTL_PROC(_hw_hn, OID_AUTO, vfmap,
5997029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 0, 0,
6007029da5cSPawel Biernacki     hn_vfmap_sysctl, "A",
6017029da5cSPawel Biernacki     "VF mapping");
602499c3e17SSepherosa Ziehau 
6039c6cae24SSepherosa Ziehau /* Transparent VF */
60478e46963SSepherosa Ziehau static int			hn_xpnt_vf = 1;
6059c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_transparent, CTLFLAG_RDTUN,
6069c6cae24SSepherosa Ziehau     &hn_xpnt_vf, 0, "Transparent VF mod");
6079c6cae24SSepherosa Ziehau 
6089c6cae24SSepherosa Ziehau /* Accurate BPF support for Transparent VF */
6099c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf = 0;
6109c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_accbpf, CTLFLAG_RDTUN,
6119c6cae24SSepherosa Ziehau     &hn_xpnt_vf_accbpf, 0, "Accurate BPF for transparent VF");
6129c6cae24SSepherosa Ziehau 
6139c6cae24SSepherosa Ziehau /* Extra wait for transparent VF attach routing; unit seconds. */
6149c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
6159c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_attwait, CTLFLAG_RWTUN,
6169c6cae24SSepherosa Ziehau     &hn_xpnt_vf_attwait, 0,
6179c6cae24SSepherosa Ziehau     "Extra wait for transparent VF attach routing; unit: seconds");
6189c6cae24SSepherosa Ziehau 
61915516c77SSepherosa Ziehau static u_int			hn_cpu_index;	/* next CPU for channel */
620fdd0222aSSepherosa Ziehau static struct taskqueue		**hn_tx_taskque;/* shared TX taskqueues */
62115516c77SSepherosa Ziehau 
622499c3e17SSepherosa Ziehau static struct rmlock		hn_vfmap_lock;
623499c3e17SSepherosa Ziehau static int			hn_vfmap_size;
6244db5958aSJustin Hibbits static if_t			*hn_vfmap;
625499c3e17SSepherosa Ziehau 
62634d68912SSepherosa Ziehau #ifndef RSS
62715516c77SSepherosa Ziehau static const uint8_t
62815516c77SSepherosa Ziehau hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
62915516c77SSepherosa Ziehau 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
63015516c77SSepherosa Ziehau 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
63115516c77SSepherosa Ziehau 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
63215516c77SSepherosa Ziehau 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
63315516c77SSepherosa Ziehau 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
63415516c77SSepherosa Ziehau };
63534d68912SSepherosa Ziehau #endif	/* !RSS */
63615516c77SSepherosa Ziehau 
637c2d50b26SSepherosa Ziehau static const struct hyperv_guid	hn_guid = {
638c2d50b26SSepherosa Ziehau 	.hv_guid = {
639c2d50b26SSepherosa Ziehau 	    0x63, 0x51, 0x61, 0xf8, 0x3e, 0xdf, 0xc5, 0x46,
640c2d50b26SSepherosa Ziehau 	    0x91, 0x3f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e }
641c2d50b26SSepherosa Ziehau };
642c2d50b26SSepherosa Ziehau 
64315516c77SSepherosa Ziehau static device_method_t hn_methods[] = {
64415516c77SSepherosa Ziehau 	/* Device interface */
64515516c77SSepherosa Ziehau 	DEVMETHOD(device_probe,		hn_probe),
64615516c77SSepherosa Ziehau 	DEVMETHOD(device_attach,	hn_attach),
64715516c77SSepherosa Ziehau 	DEVMETHOD(device_detach,	hn_detach),
64815516c77SSepherosa Ziehau 	DEVMETHOD(device_shutdown,	hn_shutdown),
64915516c77SSepherosa Ziehau 	DEVMETHOD_END
65015516c77SSepherosa Ziehau };
65115516c77SSepherosa Ziehau 
65215516c77SSepherosa Ziehau static driver_t hn_driver = {
65315516c77SSepherosa Ziehau 	"hn",
65415516c77SSepherosa Ziehau 	hn_methods,
65515516c77SSepherosa Ziehau 	sizeof(struct hn_softc)
65615516c77SSepherosa Ziehau };
65715516c77SSepherosa Ziehau 
658c1cef544SJohn Baldwin DRIVER_MODULE(hn, vmbus, hn_driver, 0, 0);
65915516c77SSepherosa Ziehau MODULE_VERSION(hn, 1);
66015516c77SSepherosa Ziehau MODULE_DEPEND(hn, vmbus, 1, 1, 1);
66115516c77SSepherosa Ziehau 
66215516c77SSepherosa Ziehau static void
66315516c77SSepherosa Ziehau hn_set_lro_lenlim(struct hn_softc *sc, int lenlim)
66415516c77SSepherosa Ziehau {
66515516c77SSepherosa Ziehau 	int i;
66615516c77SSepherosa Ziehau 
667a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
66815516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_length_lim = lenlim;
66915516c77SSepherosa Ziehau }
67015516c77SSepherosa Ziehau 
67115516c77SSepherosa Ziehau static int
67215516c77SSepherosa Ziehau hn_txpkt_sglist(struct hn_tx_ring *txr, struct hn_txdesc *txd)
67315516c77SSepherosa Ziehau {
67415516c77SSepherosa Ziehau 
67515516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
67615516c77SSepherosa Ziehau 	    txd->chim_size == 0, ("invalid rndis sglist txd"));
67715516c77SSepherosa Ziehau 	return (hn_nvs_send_rndis_sglist(txr->hn_chan, HN_NVS_RNDIS_MTYPE_DATA,
67815516c77SSepherosa Ziehau 	    &txd->send_ctx, txr->hn_gpa, txr->hn_gpa_cnt));
67915516c77SSepherosa Ziehau }
68015516c77SSepherosa Ziehau 
68115516c77SSepherosa Ziehau static int
68215516c77SSepherosa Ziehau hn_txpkt_chim(struct hn_tx_ring *txr, struct hn_txdesc *txd)
68315516c77SSepherosa Ziehau {
68415516c77SSepherosa Ziehau 	struct hn_nvs_rndis rndis;
68515516c77SSepherosa Ziehau 
68615516c77SSepherosa Ziehau 	KASSERT(txd->chim_index != HN_NVS_CHIM_IDX_INVALID &&
68715516c77SSepherosa Ziehau 	    txd->chim_size > 0, ("invalid rndis chim txd"));
68815516c77SSepherosa Ziehau 
68915516c77SSepherosa Ziehau 	rndis.nvs_type = HN_NVS_TYPE_RNDIS;
69015516c77SSepherosa Ziehau 	rndis.nvs_rndis_mtype = HN_NVS_RNDIS_MTYPE_DATA;
69115516c77SSepherosa Ziehau 	rndis.nvs_chim_idx = txd->chim_index;
69215516c77SSepherosa Ziehau 	rndis.nvs_chim_sz = txd->chim_size;
69315516c77SSepherosa Ziehau 
69415516c77SSepherosa Ziehau 	return (hn_nvs_send(txr->hn_chan, VMBUS_CHANPKT_FLAG_RC,
69515516c77SSepherosa Ziehau 	    &rndis, sizeof(rndis), &txd->send_ctx));
69615516c77SSepherosa Ziehau }
69715516c77SSepherosa Ziehau 
69815516c77SSepherosa Ziehau static __inline uint32_t
69915516c77SSepherosa Ziehau hn_chim_alloc(struct hn_softc *sc)
70015516c77SSepherosa Ziehau {
70115516c77SSepherosa Ziehau 	int i, bmap_cnt = sc->hn_chim_bmap_cnt;
70215516c77SSepherosa Ziehau 	u_long *bmap = sc->hn_chim_bmap;
70315516c77SSepherosa Ziehau 	uint32_t ret = HN_NVS_CHIM_IDX_INVALID;
70415516c77SSepherosa Ziehau 
70515516c77SSepherosa Ziehau 	for (i = 0; i < bmap_cnt; ++i) {
70615516c77SSepherosa Ziehau 		int idx;
70715516c77SSepherosa Ziehau 
70815516c77SSepherosa Ziehau 		idx = ffsl(~bmap[i]);
70915516c77SSepherosa Ziehau 		if (idx == 0)
71015516c77SSepherosa Ziehau 			continue;
71115516c77SSepherosa Ziehau 
71215516c77SSepherosa Ziehau 		--idx; /* ffsl is 1-based */
71315516c77SSepherosa Ziehau 		KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt,
71415516c77SSepherosa Ziehau 		    ("invalid i %d and idx %d", i, idx));
71515516c77SSepherosa Ziehau 
71615516c77SSepherosa Ziehau 		if (atomic_testandset_long(&bmap[i], idx))
71715516c77SSepherosa Ziehau 			continue;
71815516c77SSepherosa Ziehau 
71915516c77SSepherosa Ziehau 		ret = i * LONG_BIT + idx;
72015516c77SSepherosa Ziehau 		break;
72115516c77SSepherosa Ziehau 	}
72215516c77SSepherosa Ziehau 	return (ret);
72315516c77SSepherosa Ziehau }
72415516c77SSepherosa Ziehau 
72515516c77SSepherosa Ziehau static __inline void
72615516c77SSepherosa Ziehau hn_chim_free(struct hn_softc *sc, uint32_t chim_idx)
72715516c77SSepherosa Ziehau {
72815516c77SSepherosa Ziehau 	u_long mask;
72915516c77SSepherosa Ziehau 	uint32_t idx;
73015516c77SSepherosa Ziehau 
73115516c77SSepherosa Ziehau 	idx = chim_idx / LONG_BIT;
73215516c77SSepherosa Ziehau 	KASSERT(idx < sc->hn_chim_bmap_cnt,
73315516c77SSepherosa Ziehau 	    ("invalid chimney index 0x%x", chim_idx));
73415516c77SSepherosa Ziehau 
73515516c77SSepherosa Ziehau 	mask = 1UL << (chim_idx % LONG_BIT);
73615516c77SSepherosa Ziehau 	KASSERT(sc->hn_chim_bmap[idx] & mask,
73715516c77SSepherosa Ziehau 	    ("index bitmap 0x%lx, chimney index %u, "
73815516c77SSepherosa Ziehau 	     "bitmap idx %d, bitmask 0x%lx",
73915516c77SSepherosa Ziehau 	     sc->hn_chim_bmap[idx], chim_idx, idx, mask));
74015516c77SSepherosa Ziehau 
74115516c77SSepherosa Ziehau 	atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
74215516c77SSepherosa Ziehau }
74315516c77SSepherosa Ziehau 
744edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
745cc0c6ebcSSepherosa Ziehau 
746cc0c6ebcSSepherosa Ziehau #define PULLUP_HDR(m, len)				\
747cc0c6ebcSSepherosa Ziehau do {							\
748cc0c6ebcSSepherosa Ziehau 	if (__predict_false((m)->m_len < (len))) {	\
749cc0c6ebcSSepherosa Ziehau 		(m) = m_pullup((m), (len));		\
750cc0c6ebcSSepherosa Ziehau 		if ((m) == NULL)			\
751cc0c6ebcSSepherosa Ziehau 			return (NULL);			\
752cc0c6ebcSSepherosa Ziehau 	}						\
753cc0c6ebcSSepherosa Ziehau } while (0)
754cc0c6ebcSSepherosa Ziehau 
755edd3f315SSepherosa Ziehau /*
756edd3f315SSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
757edd3f315SSepherosa Ziehau  */
758edd3f315SSepherosa Ziehau static __inline struct mbuf *
759edd3f315SSepherosa Ziehau hn_tso_fixup(struct mbuf *m_head)
760edd3f315SSepherosa Ziehau {
761edd3f315SSepherosa Ziehau 	struct ether_vlan_header *evl;
762edd3f315SSepherosa Ziehau 	struct tcphdr *th;
763edd3f315SSepherosa Ziehau 	int ehlen;
764edd3f315SSepherosa Ziehau 
765edd3f315SSepherosa Ziehau 	KASSERT(M_WRITABLE(m_head), ("TSO mbuf not writable"));
766edd3f315SSepherosa Ziehau 
767edd3f315SSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
768edd3f315SSepherosa Ziehau 	evl = mtod(m_head, struct ether_vlan_header *);
769edd3f315SSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
770edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
771edd3f315SSepherosa Ziehau 	else
772edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
773c49d47daSSepherosa Ziehau 	m_head->m_pkthdr.l2hlen = ehlen;
774edd3f315SSepherosa Ziehau 
775edd3f315SSepherosa Ziehau #ifdef INET
776edd3f315SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
777edd3f315SSepherosa Ziehau 		struct ip *ip;
778edd3f315SSepherosa Ziehau 		int iphlen;
779edd3f315SSepherosa Ziehau 
780edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
781edd3f315SSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
782edd3f315SSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
783c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = iphlen;
784edd3f315SSepherosa Ziehau 
785edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
786edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + iphlen);
787edd3f315SSepherosa Ziehau 
788edd3f315SSepherosa Ziehau 		ip->ip_len = 0;
789edd3f315SSepherosa Ziehau 		ip->ip_sum = 0;
790edd3f315SSepherosa Ziehau 		th->th_sum = in_pseudo(ip->ip_src.s_addr,
791edd3f315SSepherosa Ziehau 		    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
792edd3f315SSepherosa Ziehau 	}
793edd3f315SSepherosa Ziehau #endif
794edd3f315SSepherosa Ziehau #if defined(INET6) && defined(INET)
795edd3f315SSepherosa Ziehau 	else
796edd3f315SSepherosa Ziehau #endif
797edd3f315SSepherosa Ziehau #ifdef INET6
798edd3f315SSepherosa Ziehau 	{
799edd3f315SSepherosa Ziehau 		struct ip6_hdr *ip6;
800edd3f315SSepherosa Ziehau 
801edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
802edd3f315SSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
803edd3f315SSepherosa Ziehau 		if (ip6->ip6_nxt != IPPROTO_TCP) {
804edd3f315SSepherosa Ziehau 			m_freem(m_head);
805edd3f315SSepherosa Ziehau 			return (NULL);
806edd3f315SSepherosa Ziehau 		}
807c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = sizeof(*ip6);
808edd3f315SSepherosa Ziehau 
809edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
810edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + sizeof(*ip6));
811edd3f315SSepherosa Ziehau 
812edd3f315SSepherosa Ziehau 		ip6->ip6_plen = 0;
813edd3f315SSepherosa Ziehau 		th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
814edd3f315SSepherosa Ziehau 	}
815edd3f315SSepherosa Ziehau #endif
816edd3f315SSepherosa Ziehau 	return (m_head);
817edd3f315SSepherosa Ziehau }
818cc0c6ebcSSepherosa Ziehau 
819cc0c6ebcSSepherosa Ziehau /*
820cc0c6ebcSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
821cc0c6ebcSSepherosa Ziehau  */
822cc0c6ebcSSepherosa Ziehau static __inline struct mbuf *
823c49d47daSSepherosa Ziehau hn_set_hlen(struct mbuf *m_head)
824cc0c6ebcSSepherosa Ziehau {
825cc0c6ebcSSepherosa Ziehau 	const struct ether_vlan_header *evl;
826cc0c6ebcSSepherosa Ziehau 	int ehlen;
827cc0c6ebcSSepherosa Ziehau 
828cc0c6ebcSSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
829cc0c6ebcSSepherosa Ziehau 	evl = mtod(m_head, const struct ether_vlan_header *);
830cc0c6ebcSSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
831cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
832cc0c6ebcSSepherosa Ziehau 	else
833cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
834c49d47daSSepherosa Ziehau 	m_head->m_pkthdr.l2hlen = ehlen;
835cc0c6ebcSSepherosa Ziehau 
836cc0c6ebcSSepherosa Ziehau #ifdef INET
837c49d47daSSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP_UDP)) {
838cc0c6ebcSSepherosa Ziehau 		const struct ip *ip;
839cc0c6ebcSSepherosa Ziehau 		int iphlen;
840cc0c6ebcSSepherosa Ziehau 
841cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
842cc0c6ebcSSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
843cc0c6ebcSSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
844c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = iphlen;
8452be266caSSepherosa Ziehau 
8462be266caSSepherosa Ziehau 		/*
8472be266caSSepherosa Ziehau 		 * UDP checksum offload does not work in Azure, if the
8482be266caSSepherosa Ziehau 		 * following conditions meet:
8492be266caSSepherosa Ziehau 		 * - sizeof(IP hdr + UDP hdr + payload) > 1420.
8502be266caSSepherosa Ziehau 		 * - IP_DF is not set in the IP hdr.
8512be266caSSepherosa Ziehau 		 *
8522be266caSSepherosa Ziehau 		 * Fallback to software checksum for these UDP datagrams.
8532be266caSSepherosa Ziehau 		 */
8542be266caSSepherosa Ziehau 		if ((m_head->m_pkthdr.csum_flags & CSUM_IP_UDP) &&
8552be266caSSepherosa Ziehau 		    m_head->m_pkthdr.len > hn_udpcs_fixup_mtu + ehlen &&
8562be266caSSepherosa Ziehau 		    (ntohs(ip->ip_off) & IP_DF) == 0) {
8572be266caSSepherosa Ziehau 			uint16_t off = ehlen + iphlen;
8582be266caSSepherosa Ziehau 
8592be266caSSepherosa Ziehau 			counter_u64_add(hn_udpcs_fixup, 1);
8602be266caSSepherosa Ziehau 			PULLUP_HDR(m_head, off + sizeof(struct udphdr));
8612be266caSSepherosa Ziehau 			*(uint16_t *)(m_head->m_data + off +
8622be266caSSepherosa Ziehau                             m_head->m_pkthdr.csum_data) = in_cksum_skip(
8632be266caSSepherosa Ziehau 			    m_head, m_head->m_pkthdr.len, off);
8642be266caSSepherosa Ziehau 			m_head->m_pkthdr.csum_flags &= ~CSUM_IP_UDP;
8652be266caSSepherosa Ziehau 		}
866cc0c6ebcSSepherosa Ziehau 	}
867cc0c6ebcSSepherosa Ziehau #endif
868cc0c6ebcSSepherosa Ziehau #if defined(INET6) && defined(INET)
869cc0c6ebcSSepherosa Ziehau 	else
870cc0c6ebcSSepherosa Ziehau #endif
871cc0c6ebcSSepherosa Ziehau #ifdef INET6
872cc0c6ebcSSepherosa Ziehau 	{
873cc0c6ebcSSepherosa Ziehau 		const struct ip6_hdr *ip6;
874cc0c6ebcSSepherosa Ziehau 
875cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
876cc0c6ebcSSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
877f0319886SWei Hu 		if (ip6->ip6_nxt != IPPROTO_TCP &&
878f0319886SWei Hu 		    ip6->ip6_nxt != IPPROTO_UDP) {
879c49d47daSSepherosa Ziehau 			m_freem(m_head);
880c49d47daSSepherosa Ziehau 			return (NULL);
881c49d47daSSepherosa Ziehau 		}
882c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = sizeof(*ip6);
883cc0c6ebcSSepherosa Ziehau 	}
884cc0c6ebcSSepherosa Ziehau #endif
885cc0c6ebcSSepherosa Ziehau 	return (m_head);
886cc0c6ebcSSepherosa Ziehau }
887cc0c6ebcSSepherosa Ziehau 
888c49d47daSSepherosa Ziehau /*
889c49d47daSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
890c49d47daSSepherosa Ziehau  */
891c49d47daSSepherosa Ziehau static __inline struct mbuf *
892c49d47daSSepherosa Ziehau hn_check_tcpsyn(struct mbuf *m_head, int *tcpsyn)
893c49d47daSSepherosa Ziehau {
894c49d47daSSepherosa Ziehau 	const struct tcphdr *th;
895c49d47daSSepherosa Ziehau 	int ehlen, iphlen;
896c49d47daSSepherosa Ziehau 
897c49d47daSSepherosa Ziehau 	*tcpsyn = 0;
898c49d47daSSepherosa Ziehau 	ehlen = m_head->m_pkthdr.l2hlen;
899c49d47daSSepherosa Ziehau 	iphlen = m_head->m_pkthdr.l3hlen;
900c49d47daSSepherosa Ziehau 
901c49d47daSSepherosa Ziehau 	PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
902c49d47daSSepherosa Ziehau 	th = mtodo(m_head, ehlen + iphlen);
903c49d47daSSepherosa Ziehau 	if (th->th_flags & TH_SYN)
904c49d47daSSepherosa Ziehau 		*tcpsyn = 1;
905c49d47daSSepherosa Ziehau 	return (m_head);
906c49d47daSSepherosa Ziehau }
907c49d47daSSepherosa Ziehau 
908cc0c6ebcSSepherosa Ziehau #undef PULLUP_HDR
909cc0c6ebcSSepherosa Ziehau 
910edd3f315SSepherosa Ziehau #endif	/* INET6 || INET */
911edd3f315SSepherosa Ziehau 
91215516c77SSepherosa Ziehau static int
913f1b0a43fSSepherosa Ziehau hn_set_rxfilter(struct hn_softc *sc, uint32_t filter)
914f1b0a43fSSepherosa Ziehau {
915f1b0a43fSSepherosa Ziehau 	int error = 0;
916f1b0a43fSSepherosa Ziehau 
917f1b0a43fSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
918f1b0a43fSSepherosa Ziehau 
919f1b0a43fSSepherosa Ziehau 	if (sc->hn_rx_filter != filter) {
920f1b0a43fSSepherosa Ziehau 		error = hn_rndis_set_rxfilter(sc, filter);
921f1b0a43fSSepherosa Ziehau 		if (!error)
922f1b0a43fSSepherosa Ziehau 			sc->hn_rx_filter = filter;
923f1b0a43fSSepherosa Ziehau 	}
924f1b0a43fSSepherosa Ziehau 	return (error);
925f1b0a43fSSepherosa Ziehau }
926f1b0a43fSSepherosa Ziehau 
927f1b0a43fSSepherosa Ziehau static int
928c08f7b2cSSepherosa Ziehau hn_rxfilter_config(struct hn_softc *sc)
92915516c77SSepherosa Ziehau {
9304db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
93115516c77SSepherosa Ziehau 	uint32_t filter;
93215516c77SSepherosa Ziehau 
93315516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
93415516c77SSepherosa Ziehau 
9359c6cae24SSepherosa Ziehau 	/*
9369c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, we don't know how
9379c6cae24SSepherosa Ziehau 	 * its RX filter is configured, so stick the synthetic device in
9389c6cae24SSepherosa Ziehau 	 * the promiscous mode.
9399c6cae24SSepherosa Ziehau 	 */
9404db5958aSJustin Hibbits 	if ((if_getflags(ifp) & IFF_PROMISC) || (sc->hn_flags & HN_FLAG_RXVF)) {
94115516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
94215516c77SSepherosa Ziehau 	} else {
94315516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_DIRECTED;
9444db5958aSJustin Hibbits 		if (if_getflags(ifp) & IFF_BROADCAST)
94515516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_BROADCAST;
94615516c77SSepherosa Ziehau 		/* TODO: support multicast list */
9474db5958aSJustin Hibbits 		if ((if_getflags(ifp) & IFF_ALLMULTI) ||
9484db5958aSJustin Hibbits 		    !if_maddr_empty(ifp))
94915516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
95015516c77SSepherosa Ziehau 	}
951f1b0a43fSSepherosa Ziehau 	return (hn_set_rxfilter(sc, filter));
95215516c77SSepherosa Ziehau }
95315516c77SSepherosa Ziehau 
954dc13fee6SSepherosa Ziehau static void
955dc13fee6SSepherosa Ziehau hn_set_txagg(struct hn_softc *sc)
956dc13fee6SSepherosa Ziehau {
957dc13fee6SSepherosa Ziehau 	uint32_t size, pkts;
958dc13fee6SSepherosa Ziehau 	int i;
959dc13fee6SSepherosa Ziehau 
960dc13fee6SSepherosa Ziehau 	/*
961dc13fee6SSepherosa Ziehau 	 * Setup aggregation size.
962dc13fee6SSepherosa Ziehau 	 */
963dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_size < 0)
964dc13fee6SSepherosa Ziehau 		size = UINT32_MAX;
965dc13fee6SSepherosa Ziehau 	else
966dc13fee6SSepherosa Ziehau 		size = sc->hn_agg_size;
967dc13fee6SSepherosa Ziehau 
968dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_size < size)
969dc13fee6SSepherosa Ziehau 		size = sc->hn_rndis_agg_size;
970dc13fee6SSepherosa Ziehau 
971a4364cfeSSepherosa Ziehau 	/* NOTE: We only aggregate packets using chimney sending buffers. */
972a4364cfeSSepherosa Ziehau 	if (size > (uint32_t)sc->hn_chim_szmax)
973a4364cfeSSepherosa Ziehau 		size = sc->hn_chim_szmax;
974a4364cfeSSepherosa Ziehau 
975dc13fee6SSepherosa Ziehau 	if (size <= 2 * HN_PKTSIZE_MIN(sc->hn_rndis_agg_align)) {
976dc13fee6SSepherosa Ziehau 		/* Disable */
977dc13fee6SSepherosa Ziehau 		size = 0;
978dc13fee6SSepherosa Ziehau 		pkts = 0;
979dc13fee6SSepherosa Ziehau 		goto done;
980dc13fee6SSepherosa Ziehau 	}
981dc13fee6SSepherosa Ziehau 
982dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'int'. */
983dc13fee6SSepherosa Ziehau 	if (size > INT_MAX)
984dc13fee6SSepherosa Ziehau 		size = INT_MAX;
985dc13fee6SSepherosa Ziehau 
986dc13fee6SSepherosa Ziehau 	/*
987dc13fee6SSepherosa Ziehau 	 * Setup aggregation packet count.
988dc13fee6SSepherosa Ziehau 	 */
989dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_pkts < 0)
990dc13fee6SSepherosa Ziehau 		pkts = UINT32_MAX;
991dc13fee6SSepherosa Ziehau 	else
992dc13fee6SSepherosa Ziehau 		pkts = sc->hn_agg_pkts;
993dc13fee6SSepherosa Ziehau 
994dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_pkts < pkts)
995dc13fee6SSepherosa Ziehau 		pkts = sc->hn_rndis_agg_pkts;
996dc13fee6SSepherosa Ziehau 
997dc13fee6SSepherosa Ziehau 	if (pkts <= 1) {
998dc13fee6SSepherosa Ziehau 		/* Disable */
999dc13fee6SSepherosa Ziehau 		size = 0;
1000dc13fee6SSepherosa Ziehau 		pkts = 0;
1001dc13fee6SSepherosa Ziehau 		goto done;
1002dc13fee6SSepherosa Ziehau 	}
1003dc13fee6SSepherosa Ziehau 
1004dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
1005dc13fee6SSepherosa Ziehau 	if (pkts > SHRT_MAX)
1006dc13fee6SSepherosa Ziehau 		pkts = SHRT_MAX;
1007dc13fee6SSepherosa Ziehau 
1008dc13fee6SSepherosa Ziehau done:
1009dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
1010dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_align > SHRT_MAX) {
1011dc13fee6SSepherosa Ziehau 		/* Disable */
1012dc13fee6SSepherosa Ziehau 		size = 0;
1013dc13fee6SSepherosa Ziehau 		pkts = 0;
1014dc13fee6SSepherosa Ziehau 	}
1015dc13fee6SSepherosa Ziehau 
1016dc13fee6SSepherosa Ziehau 	if (bootverbose) {
1017dc13fee6SSepherosa Ziehau 		if_printf(sc->hn_ifp, "TX agg size %u, pkts %u, align %u\n",
1018dc13fee6SSepherosa Ziehau 		    size, pkts, sc->hn_rndis_agg_align);
1019dc13fee6SSepherosa Ziehau 	}
1020dc13fee6SSepherosa Ziehau 
1021dc13fee6SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
1022dc13fee6SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
1023dc13fee6SSepherosa Ziehau 
1024dc13fee6SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
1025dc13fee6SSepherosa Ziehau 		txr->hn_agg_szmax = size;
1026dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktmax = pkts;
1027dc13fee6SSepherosa Ziehau 		txr->hn_agg_align = sc->hn_rndis_agg_align;
1028dc13fee6SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
1029dc13fee6SSepherosa Ziehau 	}
1030dc13fee6SSepherosa Ziehau }
1031dc13fee6SSepherosa Ziehau 
103215516c77SSepherosa Ziehau static int
103315516c77SSepherosa Ziehau hn_get_txswq_depth(const struct hn_tx_ring *txr)
103415516c77SSepherosa Ziehau {
103515516c77SSepherosa Ziehau 
103615516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_cnt > 0, ("tx ring is not setup yet"));
103715516c77SSepherosa Ziehau 	if (hn_tx_swq_depth < txr->hn_txdesc_cnt)
103815516c77SSepherosa Ziehau 		return txr->hn_txdesc_cnt;
103915516c77SSepherosa Ziehau 	return hn_tx_swq_depth;
104015516c77SSepherosa Ziehau }
104115516c77SSepherosa Ziehau 
104215516c77SSepherosa Ziehau static int
104315516c77SSepherosa Ziehau hn_rss_reconfig(struct hn_softc *sc)
104415516c77SSepherosa Ziehau {
104515516c77SSepherosa Ziehau 	int error;
104615516c77SSepherosa Ziehau 
104715516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
104815516c77SSepherosa Ziehau 
104915516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
105015516c77SSepherosa Ziehau 		return (ENXIO);
105115516c77SSepherosa Ziehau 
105215516c77SSepherosa Ziehau 	/*
105315516c77SSepherosa Ziehau 	 * Disable RSS first.
105415516c77SSepherosa Ziehau 	 *
105515516c77SSepherosa Ziehau 	 * NOTE:
105615516c77SSepherosa Ziehau 	 * Direct reconfiguration by setting the UNCHG flags does
105715516c77SSepherosa Ziehau 	 * _not_ work properly.
105815516c77SSepherosa Ziehau 	 */
105915516c77SSepherosa Ziehau 	if (bootverbose)
106015516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "disable RSS\n");
106115516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_DISABLE);
106215516c77SSepherosa Ziehau 	if (error) {
106315516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS disable failed\n");
106415516c77SSepherosa Ziehau 		return (error);
106515516c77SSepherosa Ziehau 	}
106615516c77SSepherosa Ziehau 
106715516c77SSepherosa Ziehau 	/*
106815516c77SSepherosa Ziehau 	 * Reenable the RSS w/ the updated RSS key or indirect
106915516c77SSepherosa Ziehau 	 * table.
107015516c77SSepherosa Ziehau 	 */
107115516c77SSepherosa Ziehau 	if (bootverbose)
107215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "reconfig RSS\n");
107315516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
107415516c77SSepherosa Ziehau 	if (error) {
107515516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS reconfig failed\n");
107615516c77SSepherosa Ziehau 		return (error);
107715516c77SSepherosa Ziehau 	}
107815516c77SSepherosa Ziehau 	return (0);
107915516c77SSepherosa Ziehau }
108015516c77SSepherosa Ziehau 
108115516c77SSepherosa Ziehau static void
1082afd4971bSSepherosa Ziehau hn_rss_ind_fixup(struct hn_softc *sc)
108315516c77SSepherosa Ziehau {
108415516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
1085afd4971bSSepherosa Ziehau 	int i, nchan;
108615516c77SSepherosa Ziehau 
1087afd4971bSSepherosa Ziehau 	nchan = sc->hn_rx_ring_inuse;
108815516c77SSepherosa Ziehau 	KASSERT(nchan > 1, ("invalid # of channels %d", nchan));
108915516c77SSepherosa Ziehau 
109015516c77SSepherosa Ziehau 	/*
109115516c77SSepherosa Ziehau 	 * Check indirect table to make sure that all channels in it
109215516c77SSepherosa Ziehau 	 * can be used.
109315516c77SSepherosa Ziehau 	 */
109415516c77SSepherosa Ziehau 	for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
109515516c77SSepherosa Ziehau 		if (rss->rss_ind[i] >= nchan) {
109615516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp,
109715516c77SSepherosa Ziehau 			    "RSS indirect table %d fixup: %u -> %d\n",
109815516c77SSepherosa Ziehau 			    i, rss->rss_ind[i], nchan - 1);
109915516c77SSepherosa Ziehau 			rss->rss_ind[i] = nchan - 1;
110015516c77SSepherosa Ziehau 		}
110115516c77SSepherosa Ziehau 	}
110215516c77SSepherosa Ziehau }
110315516c77SSepherosa Ziehau 
110415516c77SSepherosa Ziehau static int
11054db5958aSJustin Hibbits hn_ifmedia_upd(if_t ifp __unused)
110615516c77SSepherosa Ziehau {
110715516c77SSepherosa Ziehau 
110815516c77SSepherosa Ziehau 	return EOPNOTSUPP;
110915516c77SSepherosa Ziehau }
111015516c77SSepherosa Ziehau 
111115516c77SSepherosa Ziehau static void
11124db5958aSJustin Hibbits hn_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
111315516c77SSepherosa Ziehau {
11144db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
111515516c77SSepherosa Ziehau 
111615516c77SSepherosa Ziehau 	ifmr->ifm_status = IFM_AVALID;
111715516c77SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
111815516c77SSepherosa Ziehau 
111915516c77SSepherosa Ziehau 	if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) {
112015516c77SSepherosa Ziehau 		ifmr->ifm_active |= IFM_NONE;
112115516c77SSepherosa Ziehau 		return;
112215516c77SSepherosa Ziehau 	}
112315516c77SSepherosa Ziehau 	ifmr->ifm_status |= IFM_ACTIVE;
112415516c77SSepherosa Ziehau 	ifmr->ifm_active |= IFM_10G_T | IFM_FDX;
112515516c77SSepherosa Ziehau }
112615516c77SSepherosa Ziehau 
11275bdfd3fdSDexuan Cui static void
1128962f0357SSepherosa Ziehau hn_rxvf_set_task(void *xarg, int pending __unused)
11295bdfd3fdSDexuan Cui {
1130962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg *arg = xarg;
11315bdfd3fdSDexuan Cui 
1132962f0357SSepherosa Ziehau 	arg->rxr->hn_rxvf_ifp = arg->vf_ifp;
11335bdfd3fdSDexuan Cui }
11345bdfd3fdSDexuan Cui 
11355bdfd3fdSDexuan Cui static void
11364db5958aSJustin Hibbits hn_rxvf_set(struct hn_softc *sc, if_t vf_ifp)
11375bdfd3fdSDexuan Cui {
11385bdfd3fdSDexuan Cui 	struct hn_rx_ring *rxr;
1139962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg arg;
11405bdfd3fdSDexuan Cui 	struct task task;
11415bdfd3fdSDexuan Cui 	int i;
11425bdfd3fdSDexuan Cui 
11435bdfd3fdSDexuan Cui 	HN_LOCK_ASSERT(sc);
11445bdfd3fdSDexuan Cui 
1145962f0357SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_rxvf_set_task, &arg);
11465bdfd3fdSDexuan Cui 
11475bdfd3fdSDexuan Cui 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
11485bdfd3fdSDexuan Cui 		rxr = &sc->hn_rx_ring[i];
11495bdfd3fdSDexuan Cui 
11505bdfd3fdSDexuan Cui 		if (i < sc->hn_rx_ring_inuse) {
1151962f0357SSepherosa Ziehau 			arg.rxr = rxr;
1152962f0357SSepherosa Ziehau 			arg.vf_ifp = vf_ifp;
11535bdfd3fdSDexuan Cui 			vmbus_chan_run_task(rxr->hn_chan, &task);
11545bdfd3fdSDexuan Cui 		} else {
1155962f0357SSepherosa Ziehau 			rxr->hn_rxvf_ifp = vf_ifp;
11565bdfd3fdSDexuan Cui 		}
11575bdfd3fdSDexuan Cui 	}
11585bdfd3fdSDexuan Cui }
11595bdfd3fdSDexuan Cui 
1160962f0357SSepherosa Ziehau static bool
11614db5958aSJustin Hibbits hn_ismyvf(const struct hn_softc *sc, const if_t ifp)
1162499c3e17SSepherosa Ziehau {
11634db5958aSJustin Hibbits 	if_t hn_ifp;
1164499c3e17SSepherosa Ziehau 
1165499c3e17SSepherosa Ziehau 	hn_ifp = sc->hn_ifp;
1166499c3e17SSepherosa Ziehau 
1167499c3e17SSepherosa Ziehau 	if (ifp == hn_ifp)
1168499c3e17SSepherosa Ziehau 		return (false);
1169499c3e17SSepherosa Ziehau 
11704db5958aSJustin Hibbits 	if (if_getalloctype(ifp) != IFT_ETHER)
1171499c3e17SSepherosa Ziehau 		return (false);
1172499c3e17SSepherosa Ziehau 
1173499c3e17SSepherosa Ziehau 	/* Ignore lagg/vlan interfaces */
11744db5958aSJustin Hibbits 	if (strcmp(if_getdname(ifp), "lagg") == 0 ||
11754db5958aSJustin Hibbits 	    strcmp(if_getdname(ifp), "vlan") == 0)
1176499c3e17SSepherosa Ziehau 		return (false);
1177499c3e17SSepherosa Ziehau 
1178d76fb49fSDexuan Cui 	/*
11794db5958aSJustin Hibbits 	 * During detach events if_getifaddr(ifp) might be NULL.
1180d76fb49fSDexuan Cui 	 * Make sure the bcmp() below doesn't panic on that:
1181d76fb49fSDexuan Cui 	 */
11824db5958aSJustin Hibbits 	if (if_getifaddr(ifp) == NULL || if_getifaddr(hn_ifp) == NULL)
1183d76fb49fSDexuan Cui 		return (false);
1184d76fb49fSDexuan Cui 
11854db5958aSJustin Hibbits 	if (bcmp(if_getlladdr(ifp), if_getlladdr(hn_ifp), ETHER_ADDR_LEN) != 0)
1186499c3e17SSepherosa Ziehau 		return (false);
1187499c3e17SSepherosa Ziehau 
1188499c3e17SSepherosa Ziehau 	return (true);
1189499c3e17SSepherosa Ziehau }
1190499c3e17SSepherosa Ziehau 
11915bdfd3fdSDexuan Cui static void
11924db5958aSJustin Hibbits hn_rxvf_change(struct hn_softc *sc, if_t ifp, bool rxvf)
11935bdfd3fdSDexuan Cui {
11944db5958aSJustin Hibbits 	if_t hn_ifp;
11955bdfd3fdSDexuan Cui 
11965bdfd3fdSDexuan Cui 	HN_LOCK(sc);
11975bdfd3fdSDexuan Cui 
11985bdfd3fdSDexuan Cui 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
11995bdfd3fdSDexuan Cui 		goto out;
12005bdfd3fdSDexuan Cui 
1201499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1202499c3e17SSepherosa Ziehau 		goto out;
12035bdfd3fdSDexuan Cui 	hn_ifp = sc->hn_ifp;
12045bdfd3fdSDexuan Cui 
1205962f0357SSepherosa Ziehau 	if (rxvf) {
1206962f0357SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_RXVF)
12075bdfd3fdSDexuan Cui 			goto out;
12085bdfd3fdSDexuan Cui 
1209962f0357SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_RXVF;
12105bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
12115bdfd3fdSDexuan Cui 	} else {
1212962f0357SSepherosa Ziehau 		if (!(sc->hn_flags & HN_FLAG_RXVF))
12135bdfd3fdSDexuan Cui 			goto out;
12145bdfd3fdSDexuan Cui 
1215962f0357SSepherosa Ziehau 		sc->hn_flags &= ~HN_FLAG_RXVF;
12164db5958aSJustin Hibbits 		if (if_getdrvflags(hn_ifp) & IFF_DRV_RUNNING)
12175bdfd3fdSDexuan Cui 			hn_rxfilter_config(sc);
12185bdfd3fdSDexuan Cui 		else
12195bdfd3fdSDexuan Cui 			hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE);
12205bdfd3fdSDexuan Cui 	}
12215bdfd3fdSDexuan Cui 
12225bdfd3fdSDexuan Cui 	hn_nvs_set_datapath(sc,
12239c6cae24SSepherosa Ziehau 	    rxvf ? HN_NVS_DATAPATH_VF : HN_NVS_DATAPATH_SYNTH);
12245bdfd3fdSDexuan Cui 
1225962f0357SSepherosa Ziehau 	hn_rxvf_set(sc, rxvf ? ifp : NULL);
12265bdfd3fdSDexuan Cui 
1227962f0357SSepherosa Ziehau 	if (rxvf) {
1228642ec226SSepherosa Ziehau 		hn_vf_rss_fixup(sc, true);
12295bdfd3fdSDexuan Cui 		hn_suspend_mgmt(sc);
12305bdfd3fdSDexuan Cui 		sc->hn_link_flags &=
12315bdfd3fdSDexuan Cui 		    ~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG);
1232499c3e17SSepherosa Ziehau 		if_link_state_change(hn_ifp, LINK_STATE_DOWN);
12335bdfd3fdSDexuan Cui 	} else {
1234642ec226SSepherosa Ziehau 		hn_vf_rss_restore(sc);
12355bdfd3fdSDexuan Cui 		hn_resume_mgmt(sc);
12365bdfd3fdSDexuan Cui 	}
12375bdfd3fdSDexuan Cui 
12384db5958aSJustin Hibbits 	devctl_notify("HYPERV_NIC_VF", if_name(hn_ifp),
1239962f0357SSepherosa Ziehau 	    rxvf ? "VF_UP" : "VF_DOWN", NULL);
124033408a34SDexuan Cui 
1241962f0357SSepherosa Ziehau 	if (bootverbose) {
1242962f0357SSepherosa Ziehau 		if_printf(hn_ifp, "datapath is switched %s %s\n",
12434db5958aSJustin Hibbits 		    rxvf ? "to" : "from", if_name(ifp));
1244962f0357SSepherosa Ziehau 	}
12455bdfd3fdSDexuan Cui out:
12465bdfd3fdSDexuan Cui 	HN_UNLOCK(sc);
12475bdfd3fdSDexuan Cui }
12485bdfd3fdSDexuan Cui 
12495bdfd3fdSDexuan Cui static void
12504db5958aSJustin Hibbits hn_ifnet_event(void *arg, if_t ifp, int event)
12515bdfd3fdSDexuan Cui {
1252962f0357SSepherosa Ziehau 
12535bdfd3fdSDexuan Cui 	if (event != IFNET_EVENT_UP && event != IFNET_EVENT_DOWN)
12545bdfd3fdSDexuan Cui 		return;
1255962f0357SSepherosa Ziehau 	hn_rxvf_change(arg, ifp, event == IFNET_EVENT_UP);
12565bdfd3fdSDexuan Cui }
12575bdfd3fdSDexuan Cui 
12585bdfd3fdSDexuan Cui static void
12594db5958aSJustin Hibbits hn_ifaddr_event(void *arg, if_t ifp)
12605bdfd3fdSDexuan Cui {
1261962f0357SSepherosa Ziehau 
12624db5958aSJustin Hibbits 	hn_rxvf_change(arg, ifp, if_getflags(ifp) & IFF_UP);
12635bdfd3fdSDexuan Cui }
12645bdfd3fdSDexuan Cui 
12659c6cae24SSepherosa Ziehau static int
12669c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetcaps(struct hn_softc *sc, struct ifreq *ifr)
12679c6cae24SSepherosa Ziehau {
12684db5958aSJustin Hibbits 	if_t ifp, vf_ifp;
12699c6cae24SSepherosa Ziehau 	uint64_t tmp;
12709c6cae24SSepherosa Ziehau 	int error;
12719c6cae24SSepherosa Ziehau 
12729c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
12739c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
12749c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
12759c6cae24SSepherosa Ziehau 
12769c6cae24SSepherosa Ziehau 	/*
12779c6cae24SSepherosa Ziehau 	 * Fix up requested capabilities w/ supported capabilities,
12789c6cae24SSepherosa Ziehau 	 * since the supported capabilities could have been changed.
12799c6cae24SSepherosa Ziehau 	 */
12804db5958aSJustin Hibbits 	ifr->ifr_reqcap &= if_getcapabilities(ifp);
12819c6cae24SSepherosa Ziehau 	/* Pass SIOCSIFCAP to VF. */
12824db5958aSJustin Hibbits 	error = ifhwioctl(SIOCSIFCAP, vf_ifp, (caddr_t)ifr, curthread);
12839c6cae24SSepherosa Ziehau 
12849c6cae24SSepherosa Ziehau 	/*
12859c6cae24SSepherosa Ziehau 	 * NOTE:
12869c6cae24SSepherosa Ziehau 	 * The error will be propagated to the callers, however, it
12879c6cae24SSepherosa Ziehau 	 * is _not_ useful here.
12889c6cae24SSepherosa Ziehau 	 */
12899c6cae24SSepherosa Ziehau 
12909c6cae24SSepherosa Ziehau 	/*
12919c6cae24SSepherosa Ziehau 	 * Merge VF's enabled capabilities.
12929c6cae24SSepherosa Ziehau 	 */
12934db5958aSJustin Hibbits 	if_setcapenable(ifp, if_getcapenable(vf_ifp) & if_getcapabilities(ifp));
12949c6cae24SSepherosa Ziehau 
12954db5958aSJustin Hibbits 	tmp = if_gethwassist(vf_ifp) & HN_CSUM_IP_HWASSIST(sc);
12964db5958aSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_TXCSUM)
12974db5958aSJustin Hibbits 		if_sethwassistbits(ifp, tmp, 0);
12989c6cae24SSepherosa Ziehau 	else
12994db5958aSJustin Hibbits 		if_sethwassistbits(ifp, 0, tmp);
13009c6cae24SSepherosa Ziehau 
13014db5958aSJustin Hibbits 	tmp = if_gethwassist(vf_ifp) & HN_CSUM_IP6_HWASSIST(sc);
13024db5958aSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6)
13034db5958aSJustin Hibbits 		if_sethwassistbits(ifp, tmp, 0);
13049c6cae24SSepherosa Ziehau 	else
13054db5958aSJustin Hibbits 		if_sethwassistbits(ifp, 0, tmp);
13069c6cae24SSepherosa Ziehau 
13074db5958aSJustin Hibbits 	tmp = if_gethwassist(vf_ifp) & CSUM_IP_TSO;
13084db5958aSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_TSO4)
13094db5958aSJustin Hibbits 		if_sethwassistbits(ifp, tmp, 0);
13109c6cae24SSepherosa Ziehau 	else
13114db5958aSJustin Hibbits 		if_sethwassistbits(ifp, 0, tmp);
13129c6cae24SSepherosa Ziehau 
13134db5958aSJustin Hibbits 	tmp = if_gethwassist(vf_ifp) & CSUM_IP6_TSO;
13144db5958aSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_TSO6)
13154db5958aSJustin Hibbits 		if_sethwassistbits(ifp, tmp, 0);
13169c6cae24SSepherosa Ziehau 	else
13174db5958aSJustin Hibbits 		if_sethwassistbits(ifp, 0, tmp);
13189c6cae24SSepherosa Ziehau 
13199c6cae24SSepherosa Ziehau 	return (error);
13209c6cae24SSepherosa Ziehau }
13219c6cae24SSepherosa Ziehau 
13229c6cae24SSepherosa Ziehau static int
13239c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetflags(struct hn_softc *sc)
13249c6cae24SSepherosa Ziehau {
13254db5958aSJustin Hibbits 	if_t vf_ifp;
13269c6cae24SSepherosa Ziehau 	struct ifreq ifr;
13279c6cae24SSepherosa Ziehau 
13289c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13299c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
13309c6cae24SSepherosa Ziehau 
13319c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
13324db5958aSJustin Hibbits 	strlcpy(ifr.ifr_name, if_name(vf_ifp), sizeof(ifr.ifr_name));
13334db5958aSJustin Hibbits 	ifr.ifr_flags = if_getflags(vf_ifp) & 0xffff;
13344db5958aSJustin Hibbits 	ifr.ifr_flagshigh = if_getflags(vf_ifp) >> 16;
13354db5958aSJustin Hibbits 	return (ifhwioctl(SIOCSIFFLAGS, vf_ifp, (caddr_t)&ifr, curthread));
13369c6cae24SSepherosa Ziehau }
13379c6cae24SSepherosa Ziehau 
13389c6cae24SSepherosa Ziehau static void
13399c6cae24SSepherosa Ziehau hn_xpnt_vf_saveifflags(struct hn_softc *sc)
13409c6cae24SSepherosa Ziehau {
13414db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
13429c6cae24SSepherosa Ziehau 	int allmulti = 0;
13439c6cae24SSepherosa Ziehau 
13449c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13459c6cae24SSepherosa Ziehau 
13469c6cae24SSepherosa Ziehau 	/* XXX vlan(4) style mcast addr maintenance */
13474db5958aSJustin Hibbits 	if (!if_maddr_empty(ifp))
13489c6cae24SSepherosa Ziehau 		allmulti = IFF_ALLMULTI;
13499c6cae24SSepherosa Ziehau 
13509c6cae24SSepherosa Ziehau 	/* Always set the VF's if_flags */
13514db5958aSJustin Hibbits 	if_setflags(sc->hn_vf_ifp, if_getflags(ifp) | allmulti);
13529c6cae24SSepherosa Ziehau }
13539c6cae24SSepherosa Ziehau 
13549c6cae24SSepherosa Ziehau static void
13554db5958aSJustin Hibbits hn_xpnt_vf_input(if_t vf_ifp, struct mbuf *m)
13569c6cae24SSepherosa Ziehau {
13579c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
13584db5958aSJustin Hibbits 	if_t hn_ifp = NULL;
13599c6cae24SSepherosa Ziehau 	struct mbuf *mn;
13609c6cae24SSepherosa Ziehau 
13619c6cae24SSepherosa Ziehau 	/*
13629c6cae24SSepherosa Ziehau 	 * XXX racy, if hn(4) ever detached.
13639c6cae24SSepherosa Ziehau 	 */
13649c6cae24SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
13654db5958aSJustin Hibbits 	if (if_getindex(vf_ifp) < hn_vfmap_size)
13664db5958aSJustin Hibbits 		hn_ifp = hn_vfmap[if_getindex(vf_ifp)];
13679c6cae24SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
13689c6cae24SSepherosa Ziehau 
13699c6cae24SSepherosa Ziehau 	if (hn_ifp != NULL) {
13709c6cae24SSepherosa Ziehau 		for (mn = m; mn != NULL; mn = mn->m_nextpkt) {
13713bed4e54SSepherosa Ziehau 			/*
13723bed4e54SSepherosa Ziehau 			 * Allow tapping on the VF.
13733bed4e54SSepherosa Ziehau 			 */
13749c6cae24SSepherosa Ziehau 			ETHER_BPF_MTAP(vf_ifp, mn);
13753bed4e54SSepherosa Ziehau 
13763bed4e54SSepherosa Ziehau 			/*
13773bed4e54SSepherosa Ziehau 			 * Update VF stats.
13783bed4e54SSepherosa Ziehau 			 */
13794db5958aSJustin Hibbits 			if ((if_getcapenable(vf_ifp) & IFCAP_HWSTATS) == 0) {
13803bed4e54SSepherosa Ziehau 				if_inc_counter(vf_ifp, IFCOUNTER_IBYTES,
13813bed4e54SSepherosa Ziehau 				    mn->m_pkthdr.len);
13823bed4e54SSepherosa Ziehau 			}
13833bed4e54SSepherosa Ziehau 			/*
13843bed4e54SSepherosa Ziehau 			 * XXX IFCOUNTER_IMCAST
13853bed4e54SSepherosa Ziehau 			 * This stat updating is kinda invasive, since it
13863bed4e54SSepherosa Ziehau 			 * requires two checks on the mbuf: the length check
13873bed4e54SSepherosa Ziehau 			 * and the ethernet header check.  As of this write,
13883bed4e54SSepherosa Ziehau 			 * all multicast packets go directly to hn(4), which
13893bed4e54SSepherosa Ziehau 			 * makes imcast stat updating in the VF a try in vian.
13903bed4e54SSepherosa Ziehau 			 */
13913bed4e54SSepherosa Ziehau 
13923bed4e54SSepherosa Ziehau 			/*
13933bed4e54SSepherosa Ziehau 			 * Fix up rcvif and increase hn(4)'s ipackets.
13943bed4e54SSepherosa Ziehau 			 */
13959c6cae24SSepherosa Ziehau 			mn->m_pkthdr.rcvif = hn_ifp;
13969c6cae24SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
13979c6cae24SSepherosa Ziehau 		}
13983bed4e54SSepherosa Ziehau 		/*
13993bed4e54SSepherosa Ziehau 		 * Go through hn(4)'s if_input.
14003bed4e54SSepherosa Ziehau 		 */
14014db5958aSJustin Hibbits 		if_input(hn_ifp, m);
14029c6cae24SSepherosa Ziehau 	} else {
14039c6cae24SSepherosa Ziehau 		/*
14049c6cae24SSepherosa Ziehau 		 * In the middle of the transition; free this
14059c6cae24SSepherosa Ziehau 		 * mbuf chain.
14069c6cae24SSepherosa Ziehau 		 */
14079c6cae24SSepherosa Ziehau 		while (m != NULL) {
14089c6cae24SSepherosa Ziehau 			mn = m->m_nextpkt;
14099c6cae24SSepherosa Ziehau 			m->m_nextpkt = NULL;
14109c6cae24SSepherosa Ziehau 			m_freem(m);
14119c6cae24SSepherosa Ziehau 			m = mn;
14129c6cae24SSepherosa Ziehau 		}
14139c6cae24SSepherosa Ziehau 	}
14149c6cae24SSepherosa Ziehau }
14159c6cae24SSepherosa Ziehau 
14169c6cae24SSepherosa Ziehau static void
14179c6cae24SSepherosa Ziehau hn_mtu_change_fixup(struct hn_softc *sc)
14189c6cae24SSepherosa Ziehau {
14194db5958aSJustin Hibbits 	if_t ifp;
14209c6cae24SSepherosa Ziehau 
14219c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
14229c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
14239c6cae24SSepherosa Ziehau 
14244db5958aSJustin Hibbits 	hn_set_tso_maxsize(sc, hn_tso_maxlen, if_getmtu(ifp));
14259c6cae24SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_lro.lro_length_lim < HN_LRO_LENLIM_MIN(ifp))
14269c6cae24SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp));
14279c6cae24SSepherosa Ziehau }
14289c6cae24SSepherosa Ziehau 
1429642ec226SSepherosa Ziehau static uint32_t
1430642ec226SSepherosa Ziehau hn_rss_type_fromndis(uint32_t rss_hash)
1431642ec226SSepherosa Ziehau {
1432642ec226SSepherosa Ziehau 	uint32_t types = 0;
1433642ec226SSepherosa Ziehau 
1434642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV4)
1435642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV4;
1436642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV4)
1437642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV4;
1438642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV6)
1439642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV6;
1440642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV6_EX)
1441642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV6_EX;
1442642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV6)
1443642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV6;
1444642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV6_EX)
1445642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV6_EX;
14466f12c42eSSepherosa Ziehau 	if (rss_hash & NDIS_HASH_UDP_IPV4_X)
14476f12c42eSSepherosa Ziehau 		types |= RSS_TYPE_UDP_IPV4;
1448642ec226SSepherosa Ziehau 	return (types);
1449642ec226SSepherosa Ziehau }
1450642ec226SSepherosa Ziehau 
1451642ec226SSepherosa Ziehau static uint32_t
1452642ec226SSepherosa Ziehau hn_rss_type_tondis(uint32_t types)
1453642ec226SSepherosa Ziehau {
1454642ec226SSepherosa Ziehau 	uint32_t rss_hash = 0;
1455642ec226SSepherosa Ziehau 
14566f12c42eSSepherosa Ziehau 	KASSERT((types & (RSS_TYPE_UDP_IPV6 | RSS_TYPE_UDP_IPV6_EX)) == 0,
14576f12c42eSSepherosa Ziehau 	    ("UDP6 and UDP6EX are not supported"));
1458642ec226SSepherosa Ziehau 
1459642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV4)
1460642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV4;
1461642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV4)
1462642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV4;
1463642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV6)
1464642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV6;
1465642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV6_EX)
1466642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV6_EX;
1467642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV6)
1468642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV6;
1469642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV6_EX)
1470642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV6_EX;
14716f12c42eSSepherosa Ziehau 	if (types & RSS_TYPE_UDP_IPV4)
14726f12c42eSSepherosa Ziehau 		rss_hash |= NDIS_HASH_UDP_IPV4_X;
1473642ec226SSepherosa Ziehau 	return (rss_hash);
1474642ec226SSepherosa Ziehau }
1475642ec226SSepherosa Ziehau 
1476642ec226SSepherosa Ziehau static void
1477642ec226SSepherosa Ziehau hn_rss_mbuf_hash(struct hn_softc *sc, uint32_t mbuf_hash)
1478642ec226SSepherosa Ziehau {
1479642ec226SSepherosa Ziehau 	int i;
1480642ec226SSepherosa Ziehau 
1481642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1482642ec226SSepherosa Ziehau 
1483642ec226SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1484642ec226SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_mbuf_hash = mbuf_hash;
1485642ec226SSepherosa Ziehau }
1486642ec226SSepherosa Ziehau 
1487642ec226SSepherosa Ziehau static void
1488642ec226SSepherosa Ziehau hn_vf_rss_fixup(struct hn_softc *sc, bool reconf)
1489642ec226SSepherosa Ziehau {
14904db5958aSJustin Hibbits 	if_t ifp, vf_ifp;
1491642ec226SSepherosa Ziehau 	struct ifrsshash ifrh;
1492642ec226SSepherosa Ziehau 	struct ifrsskey ifrk;
1493642ec226SSepherosa Ziehau 	int error;
1494642ec226SSepherosa Ziehau 	uint32_t my_types, diff_types, mbuf_types = 0;
1495642ec226SSepherosa Ziehau 
1496642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1497642ec226SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
14984db5958aSJustin Hibbits 	    ("%s: synthetic parts are not attached", if_name(sc->hn_ifp)));
1499642ec226SSepherosa Ziehau 
1500642ec226SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
1501642ec226SSepherosa Ziehau 		/* No RSS on synthetic parts; done. */
1502642ec226SSepherosa Ziehau 		return;
1503642ec226SSepherosa Ziehau 	}
1504642ec226SSepherosa Ziehau 	if ((sc->hn_rss_hcap & NDIS_HASH_FUNCTION_TOEPLITZ) == 0) {
1505642ec226SSepherosa Ziehau 		/* Synthetic parts do not support Toeplitz; done. */
1506642ec226SSepherosa Ziehau 		return;
1507642ec226SSepherosa Ziehau 	}
1508642ec226SSepherosa Ziehau 
1509642ec226SSepherosa Ziehau 	ifp = sc->hn_ifp;
1510642ec226SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
1511642ec226SSepherosa Ziehau 
1512642ec226SSepherosa Ziehau 	/*
1513642ec226SSepherosa Ziehau 	 * Extract VF's RSS key.  Only 40 bytes key for Toeplitz is
1514642ec226SSepherosa Ziehau 	 * supported.
1515642ec226SSepherosa Ziehau 	 */
1516642ec226SSepherosa Ziehau 	memset(&ifrk, 0, sizeof(ifrk));
15174db5958aSJustin Hibbits 	strlcpy(ifrk.ifrk_name, if_name(vf_ifp), sizeof(ifrk.ifrk_name));
15184db5958aSJustin Hibbits 	error = ifhwioctl(SIOCGIFRSSKEY, vf_ifp, (caddr_t)&ifrk, curthread);
1519642ec226SSepherosa Ziehau 	if (error) {
1520a3b413afSHans Petter Selasky 		if_printf(ifp, "%s SIOCGIFRSSKEY failed: %d\n",
15214db5958aSJustin Hibbits 		    if_name(vf_ifp), error);
1522642ec226SSepherosa Ziehau 		goto done;
1523642ec226SSepherosa Ziehau 	}
1524642ec226SSepherosa Ziehau 	if (ifrk.ifrk_func != RSS_FUNC_TOEPLITZ) {
1525642ec226SSepherosa Ziehau 		if_printf(ifp, "%s RSS function %u is not Toeplitz\n",
15264db5958aSJustin Hibbits 		    if_name(vf_ifp), ifrk.ifrk_func);
1527642ec226SSepherosa Ziehau 		goto done;
1528642ec226SSepherosa Ziehau 	}
1529642ec226SSepherosa Ziehau 	if (ifrk.ifrk_keylen != NDIS_HASH_KEYSIZE_TOEPLITZ) {
1530642ec226SSepherosa Ziehau 		if_printf(ifp, "%s invalid RSS Toeplitz key length %d\n",
15314db5958aSJustin Hibbits 		    if_name(vf_ifp), ifrk.ifrk_keylen);
1532642ec226SSepherosa Ziehau 		goto done;
1533642ec226SSepherosa Ziehau 	}
1534642ec226SSepherosa Ziehau 
1535642ec226SSepherosa Ziehau 	/*
1536642ec226SSepherosa Ziehau 	 * Extract VF's RSS hash.  Only Toeplitz is supported.
1537642ec226SSepherosa Ziehau 	 */
1538642ec226SSepherosa Ziehau 	memset(&ifrh, 0, sizeof(ifrh));
15394db5958aSJustin Hibbits 	strlcpy(ifrh.ifrh_name, if_name(vf_ifp), sizeof(ifrh.ifrh_name));
15404db5958aSJustin Hibbits 	error = ifhwioctl(SIOCGIFRSSHASH, vf_ifp, (caddr_t)&ifrh, curthread);
1541642ec226SSepherosa Ziehau 	if (error) {
1542642ec226SSepherosa Ziehau 		if_printf(ifp, "%s SIOCGRSSHASH failed: %d\n",
15434db5958aSJustin Hibbits 		    if_name(vf_ifp), error);
1544642ec226SSepherosa Ziehau 		goto done;
1545642ec226SSepherosa Ziehau 	}
1546642ec226SSepherosa Ziehau 	if (ifrh.ifrh_func != RSS_FUNC_TOEPLITZ) {
1547642ec226SSepherosa Ziehau 		if_printf(ifp, "%s RSS function %u is not Toeplitz\n",
15484db5958aSJustin Hibbits 		    if_name(vf_ifp), ifrh.ifrh_func);
1549642ec226SSepherosa Ziehau 		goto done;
1550642ec226SSepherosa Ziehau 	}
1551642ec226SSepherosa Ziehau 
1552642ec226SSepherosa Ziehau 	my_types = hn_rss_type_fromndis(sc->hn_rss_hcap);
1553642ec226SSepherosa Ziehau 	if ((ifrh.ifrh_types & my_types) == 0) {
1554642ec226SSepherosa Ziehau 		/* This disables RSS; ignore it then */
1555642ec226SSepherosa Ziehau 		if_printf(ifp, "%s intersection of RSS types failed.  "
15564db5958aSJustin Hibbits 		    "VF %#x, mine %#x\n", if_name(vf_ifp),
1557642ec226SSepherosa Ziehau 		    ifrh.ifrh_types, my_types);
1558642ec226SSepherosa Ziehau 		goto done;
1559642ec226SSepherosa Ziehau 	}
1560642ec226SSepherosa Ziehau 
1561642ec226SSepherosa Ziehau 	diff_types = my_types ^ ifrh.ifrh_types;
1562642ec226SSepherosa Ziehau 	my_types &= ifrh.ifrh_types;
1563642ec226SSepherosa Ziehau 	mbuf_types = my_types;
1564642ec226SSepherosa Ziehau 
1565642ec226SSepherosa Ziehau 	/*
1566642ec226SSepherosa Ziehau 	 * Detect RSS hash value/type confliction.
1567642ec226SSepherosa Ziehau 	 *
1568642ec226SSepherosa Ziehau 	 * NOTE:
1569642ec226SSepherosa Ziehau 	 * We don't disable the hash type, but stop delivery the hash
1570642ec226SSepherosa Ziehau 	 * value/type through mbufs on RX path.
15716f12c42eSSepherosa Ziehau 	 *
15726f12c42eSSepherosa Ziehau 	 * XXX If HN_CAP_UDPHASH is set in hn_caps, then UDP 4-tuple
15736f12c42eSSepherosa Ziehau 	 * hash is delivered with type of TCP_IPV4.  This means if
15746f12c42eSSepherosa Ziehau 	 * UDP_IPV4 is enabled, then TCP_IPV4 should be forced, at
15756f12c42eSSepherosa Ziehau 	 * least to hn_mbuf_hash.  However, given that _all_ of the
15766f12c42eSSepherosa Ziehau 	 * NICs implement TCP_IPV4, this will _not_ impose any issues
15776f12c42eSSepherosa Ziehau 	 * here.
1578642ec226SSepherosa Ziehau 	 */
1579642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV4) &&
1580642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1581642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV4 | RSS_TYPE_UDP_IPV4))) {
1582642ec226SSepherosa Ziehau 		/* Conflict; disable IPV4 hash type/value delivery. */
1583642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV4 mbuf hash delivery\n");
1584642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV4;
1585642ec226SSepherosa Ziehau 	}
1586642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV6) &&
1587642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1588642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV6 | RSS_TYPE_UDP_IPV6 |
1589642ec226SSepherosa Ziehau 	      RSS_TYPE_TCP_IPV6_EX | RSS_TYPE_UDP_IPV6_EX |
1590642ec226SSepherosa Ziehau 	      RSS_TYPE_IPV6_EX))) {
1591642ec226SSepherosa Ziehau 		/* Conflict; disable IPV6 hash type/value delivery. */
1592642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV6 mbuf hash delivery\n");
1593642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV6;
1594642ec226SSepherosa Ziehau 	}
1595642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV6_EX) &&
1596642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1597642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV6 | RSS_TYPE_UDP_IPV6 |
1598642ec226SSepherosa Ziehau 	      RSS_TYPE_TCP_IPV6_EX | RSS_TYPE_UDP_IPV6_EX |
1599642ec226SSepherosa Ziehau 	      RSS_TYPE_IPV6))) {
1600642ec226SSepherosa Ziehau 		/* Conflict; disable IPV6_EX hash type/value delivery. */
1601642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV6_EX mbuf hash delivery\n");
1602642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV6_EX;
1603642ec226SSepherosa Ziehau 	}
1604642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_TCP_IPV6) &&
1605642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_TCP_IPV6_EX)) {
1606642ec226SSepherosa Ziehau 		/* Conflict; disable TCP_IPV6 hash type/value delivery. */
1607642ec226SSepherosa Ziehau 		if_printf(ifp, "disable TCP_IPV6 mbuf hash delivery\n");
1608642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_TCP_IPV6;
1609642ec226SSepherosa Ziehau 	}
1610642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_TCP_IPV6_EX) &&
1611642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_TCP_IPV6)) {
1612642ec226SSepherosa Ziehau 		/* Conflict; disable TCP_IPV6_EX hash type/value delivery. */
1613642ec226SSepherosa Ziehau 		if_printf(ifp, "disable TCP_IPV6_EX mbuf hash delivery\n");
1614642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_TCP_IPV6_EX;
1615642ec226SSepherosa Ziehau 	}
1616642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_UDP_IPV6) &&
1617642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_UDP_IPV6_EX)) {
1618642ec226SSepherosa Ziehau 		/* Conflict; disable UDP_IPV6 hash type/value delivery. */
1619642ec226SSepherosa Ziehau 		if_printf(ifp, "disable UDP_IPV6 mbuf hash delivery\n");
1620642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_UDP_IPV6;
1621642ec226SSepherosa Ziehau 	}
1622642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_UDP_IPV6_EX) &&
1623642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_UDP_IPV6)) {
1624642ec226SSepherosa Ziehau 		/* Conflict; disable UDP_IPV6_EX hash type/value delivery. */
1625642ec226SSepherosa Ziehau 		if_printf(ifp, "disable UDP_IPV6_EX mbuf hash delivery\n");
1626642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_UDP_IPV6_EX;
1627642ec226SSepherosa Ziehau 	}
1628642ec226SSepherosa Ziehau 
1629642ec226SSepherosa Ziehau 	/*
1630642ec226SSepherosa Ziehau 	 * Indirect table does not matter.
1631642ec226SSepherosa Ziehau 	 */
1632642ec226SSepherosa Ziehau 
1633642ec226SSepherosa Ziehau 	sc->hn_rss_hash = (sc->hn_rss_hcap & NDIS_HASH_FUNCTION_MASK) |
1634642ec226SSepherosa Ziehau 	    hn_rss_type_tondis(my_types);
1635642ec226SSepherosa Ziehau 	memcpy(sc->hn_rss.rss_key, ifrk.ifrk_key, sizeof(sc->hn_rss.rss_key));
1636642ec226SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
1637642ec226SSepherosa Ziehau 
1638642ec226SSepherosa Ziehau 	if (reconf) {
1639642ec226SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
1640642ec226SSepherosa Ziehau 		if (error) {
1641642ec226SSepherosa Ziehau 			/* XXX roll-back? */
1642642ec226SSepherosa Ziehau 			if_printf(ifp, "hn_rss_reconfig failed: %d\n", error);
1643642ec226SSepherosa Ziehau 			/* XXX keep going. */
1644642ec226SSepherosa Ziehau 		}
1645642ec226SSepherosa Ziehau 	}
1646642ec226SSepherosa Ziehau done:
1647642ec226SSepherosa Ziehau 	/* Hash deliverability for mbufs. */
1648642ec226SSepherosa Ziehau 	hn_rss_mbuf_hash(sc, hn_rss_type_tondis(mbuf_types));
1649642ec226SSepherosa Ziehau }
1650642ec226SSepherosa Ziehau 
1651642ec226SSepherosa Ziehau static void
1652642ec226SSepherosa Ziehau hn_vf_rss_restore(struct hn_softc *sc)
1653642ec226SSepherosa Ziehau {
1654642ec226SSepherosa Ziehau 
1655642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1656642ec226SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
16574db5958aSJustin Hibbits 	    ("%s: synthetic parts are not attached", if_name(sc->hn_ifp)));
1658642ec226SSepherosa Ziehau 
1659642ec226SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1)
1660642ec226SSepherosa Ziehau 		goto done;
1661642ec226SSepherosa Ziehau 
1662642ec226SSepherosa Ziehau 	/*
1663642ec226SSepherosa Ziehau 	 * Restore hash types.  Key does _not_ matter.
1664642ec226SSepherosa Ziehau 	 */
1665642ec226SSepherosa Ziehau 	if (sc->hn_rss_hash != sc->hn_rss_hcap) {
1666642ec226SSepherosa Ziehau 		int error;
1667642ec226SSepherosa Ziehau 
1668642ec226SSepherosa Ziehau 		sc->hn_rss_hash = sc->hn_rss_hcap;
1669642ec226SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
1670642ec226SSepherosa Ziehau 		if (error) {
1671642ec226SSepherosa Ziehau 			if_printf(sc->hn_ifp, "hn_rss_reconfig failed: %d\n",
1672642ec226SSepherosa Ziehau 			    error);
1673642ec226SSepherosa Ziehau 			/* XXX keep going. */
1674642ec226SSepherosa Ziehau 		}
1675642ec226SSepherosa Ziehau 	}
1676642ec226SSepherosa Ziehau done:
1677642ec226SSepherosa Ziehau 	/* Hash deliverability for mbufs. */
1678642ec226SSepherosa Ziehau 	hn_rss_mbuf_hash(sc, NDIS_HASH_ALL);
1679642ec226SSepherosa Ziehau }
1680642ec226SSepherosa Ziehau 
16819c6cae24SSepherosa Ziehau static void
16829c6cae24SSepherosa Ziehau hn_xpnt_vf_setready(struct hn_softc *sc)
16839c6cae24SSepherosa Ziehau {
16844db5958aSJustin Hibbits 	if_t ifp, vf_ifp;
16859c6cae24SSepherosa Ziehau 	struct ifreq ifr;
16869c6cae24SSepherosa Ziehau 
16879c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
16889c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
16899c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
16909c6cae24SSepherosa Ziehau 
16919c6cae24SSepherosa Ziehau 	/*
16929c6cae24SSepherosa Ziehau 	 * Mark the VF ready.
16939c6cae24SSepherosa Ziehau 	 */
16949c6cae24SSepherosa Ziehau 	sc->hn_vf_rdytick = 0;
16959c6cae24SSepherosa Ziehau 
16969c6cae24SSepherosa Ziehau 	/*
16979c6cae24SSepherosa Ziehau 	 * Save information for restoration.
16989c6cae24SSepherosa Ziehau 	 */
16994db5958aSJustin Hibbits 	sc->hn_saved_caps = if_getcapabilities(ifp);
17004db5958aSJustin Hibbits 	sc->hn_saved_tsomax = if_gethwtsomax(ifp);
17014db5958aSJustin Hibbits 	sc->hn_saved_tsosegcnt = if_gethwtsomaxsegcount(ifp);
17024db5958aSJustin Hibbits 	sc->hn_saved_tsosegsz = if_gethwtsomaxsegsize(ifp);
17039c6cae24SSepherosa Ziehau 
17049c6cae24SSepherosa Ziehau 	/*
17059c6cae24SSepherosa Ziehau 	 * Intersect supported/enabled capabilities.
17069c6cae24SSepherosa Ziehau 	 *
17079c6cae24SSepherosa Ziehau 	 * NOTE:
17089c6cae24SSepherosa Ziehau 	 * if_hwassist is not changed here.
17099c6cae24SSepherosa Ziehau 	 */
17104db5958aSJustin Hibbits 	if_setcapabilitiesbit(ifp, 0, if_getcapabilities(vf_ifp));
17114db5958aSJustin Hibbits 	if_setcapenablebit(ifp, 0, if_getcapabilities(ifp));
17129c6cae24SSepherosa Ziehau 
17139c6cae24SSepherosa Ziehau 	/*
17149c6cae24SSepherosa Ziehau 	 * Fix TSO settings.
17159c6cae24SSepherosa Ziehau 	 */
17164db5958aSJustin Hibbits 	if (if_gethwtsomax(ifp) > if_gethwtsomax(vf_ifp))
17174db5958aSJustin Hibbits 		if_sethwtsomax(ifp, if_gethwtsomax(vf_ifp));
17184db5958aSJustin Hibbits 	if (if_gethwtsomaxsegcount(ifp) > if_gethwtsomaxsegcount(vf_ifp))
17194db5958aSJustin Hibbits 		if_sethwtsomaxsegcount(ifp, if_gethwtsomaxsegcount(vf_ifp));
17204db5958aSJustin Hibbits 	if (if_gethwtsomaxsegsize(ifp) > if_gethwtsomaxsegsize(vf_ifp))
17214db5958aSJustin Hibbits 		if_sethwtsomaxsegsize(ifp, if_gethwtsomaxsegsize(vf_ifp));
17229c6cae24SSepherosa Ziehau 
17239c6cae24SSepherosa Ziehau 	/*
17249c6cae24SSepherosa Ziehau 	 * Change VF's enabled capabilities.
17259c6cae24SSepherosa Ziehau 	 */
17269c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
17274db5958aSJustin Hibbits 	strlcpy(ifr.ifr_name, if_name(vf_ifp), sizeof(ifr.ifr_name));
17284db5958aSJustin Hibbits 	ifr.ifr_reqcap = if_getcapenable(ifp);
17299c6cae24SSepherosa Ziehau 	hn_xpnt_vf_iocsetcaps(sc, &ifr);
17309c6cae24SSepherosa Ziehau 
17314db5958aSJustin Hibbits 	if (if_getmtu(ifp) != ETHERMTU) {
17329c6cae24SSepherosa Ziehau 		int error;
17339c6cae24SSepherosa Ziehau 
17349c6cae24SSepherosa Ziehau 		/*
17359c6cae24SSepherosa Ziehau 		 * Change VF's MTU.
17369c6cae24SSepherosa Ziehau 		 */
17379c6cae24SSepherosa Ziehau 		memset(&ifr, 0, sizeof(ifr));
17384db5958aSJustin Hibbits 		strlcpy(ifr.ifr_name, if_name(vf_ifp), sizeof(ifr.ifr_name));
17394db5958aSJustin Hibbits 		ifr.ifr_mtu = if_getmtu(ifp);
17404db5958aSJustin Hibbits 		error = ifhwioctl(SIOCSIFMTU, vf_ifp, (caddr_t)&ifr, curthread);
17419c6cae24SSepherosa Ziehau 		if (error) {
17429c6cae24SSepherosa Ziehau 			if_printf(ifp, "%s SIOCSIFMTU %u failed\n",
17434db5958aSJustin Hibbits 			    if_name(vf_ifp), if_getmtu(ifp));
17444db5958aSJustin Hibbits 			if (if_getmtu(ifp) > ETHERMTU) {
17459c6cae24SSepherosa Ziehau 				if_printf(ifp, "change MTU to %d\n", ETHERMTU);
17469c6cae24SSepherosa Ziehau 
17479c6cae24SSepherosa Ziehau 				/*
17489c6cae24SSepherosa Ziehau 				 * XXX
17499c6cae24SSepherosa Ziehau 				 * No need to adjust the synthetic parts' MTU;
17509c6cae24SSepherosa Ziehau 				 * failure of the adjustment will cause us
17519c6cae24SSepherosa Ziehau 				 * infinite headache.
17529c6cae24SSepherosa Ziehau 				 */
17534db5958aSJustin Hibbits 				if_setmtu(ifp, ETHERMTU);
17549c6cae24SSepherosa Ziehau 				hn_mtu_change_fixup(sc);
17559c6cae24SSepherosa Ziehau 			}
17569c6cae24SSepherosa Ziehau 		}
17579c6cae24SSepherosa Ziehau 	}
17589c6cae24SSepherosa Ziehau }
17599c6cae24SSepherosa Ziehau 
17609c6cae24SSepherosa Ziehau static bool
17619c6cae24SSepherosa Ziehau hn_xpnt_vf_isready(struct hn_softc *sc)
17629c6cae24SSepherosa Ziehau {
17639c6cae24SSepherosa Ziehau 
17649c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
17659c6cae24SSepherosa Ziehau 
17669c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf || sc->hn_vf_ifp == NULL)
17679c6cae24SSepherosa Ziehau 		return (false);
17689c6cae24SSepherosa Ziehau 
17699c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick == 0)
17709c6cae24SSepherosa Ziehau 		return (true);
17719c6cae24SSepherosa Ziehau 
17729c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick > ticks)
17739c6cae24SSepherosa Ziehau 		return (false);
17749c6cae24SSepherosa Ziehau 
17759c6cae24SSepherosa Ziehau 	/* Mark VF as ready. */
17769c6cae24SSepherosa Ziehau 	hn_xpnt_vf_setready(sc);
17779c6cae24SSepherosa Ziehau 	return (true);
17789c6cae24SSepherosa Ziehau }
17799c6cae24SSepherosa Ziehau 
17809c6cae24SSepherosa Ziehau static void
1781a97fff19SSepherosa Ziehau hn_xpnt_vf_setenable(struct hn_softc *sc)
1782a97fff19SSepherosa Ziehau {
1783a97fff19SSepherosa Ziehau 	int i;
1784a97fff19SSepherosa Ziehau 
1785a97fff19SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1786a97fff19SSepherosa Ziehau 
1787a97fff19SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
1788a97fff19SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
1789a97fff19SSepherosa Ziehau 	sc->hn_xvf_flags |= HN_XVFFLAG_ENABLED;
1790a97fff19SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1791a97fff19SSepherosa Ziehau 
1792a97fff19SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1793a97fff19SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_XPNT_VF;
1794a97fff19SSepherosa Ziehau }
1795a97fff19SSepherosa Ziehau 
1796a97fff19SSepherosa Ziehau static void
1797a97fff19SSepherosa Ziehau hn_xpnt_vf_setdisable(struct hn_softc *sc, bool clear_vf)
1798a97fff19SSepherosa Ziehau {
1799a97fff19SSepherosa Ziehau 	int i;
1800a97fff19SSepherosa Ziehau 
1801a97fff19SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1802a97fff19SSepherosa Ziehau 
1803a97fff19SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
1804a97fff19SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
1805a97fff19SSepherosa Ziehau 	sc->hn_xvf_flags &= ~HN_XVFFLAG_ENABLED;
1806a97fff19SSepherosa Ziehau 	if (clear_vf)
1807a97fff19SSepherosa Ziehau 		sc->hn_vf_ifp = NULL;
1808a97fff19SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1809a97fff19SSepherosa Ziehau 
1810a97fff19SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1811a97fff19SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_rx_flags &= ~HN_RX_FLAG_XPNT_VF;
1812a97fff19SSepherosa Ziehau }
1813a97fff19SSepherosa Ziehau 
1814a97fff19SSepherosa Ziehau static void
18159c6cae24SSepherosa Ziehau hn_xpnt_vf_init(struct hn_softc *sc)
18169c6cae24SSepherosa Ziehau {
18179c6cae24SSepherosa Ziehau 	int error;
18189c6cae24SSepherosa Ziehau 
18199c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
18209c6cae24SSepherosa Ziehau 
18219c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
18224db5958aSJustin Hibbits 	    ("%s: transparent VF was enabled", if_name(sc->hn_ifp)));
18239c6cae24SSepherosa Ziehau 
18249c6cae24SSepherosa Ziehau 	if (bootverbose) {
18259c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "try bringing up %s\n",
18264db5958aSJustin Hibbits 		    if_name(sc->hn_vf_ifp));
18279c6cae24SSepherosa Ziehau 	}
18289c6cae24SSepherosa Ziehau 
18299c6cae24SSepherosa Ziehau 	/*
18309c6cae24SSepherosa Ziehau 	 * Bring the VF up.
18319c6cae24SSepherosa Ziehau 	 */
18329c6cae24SSepherosa Ziehau 	hn_xpnt_vf_saveifflags(sc);
18334db5958aSJustin Hibbits 	if_setflagbits(sc->hn_ifp, IFF_UP, 0);
18349c6cae24SSepherosa Ziehau 	error = hn_xpnt_vf_iocsetflags(sc);
18359c6cae24SSepherosa Ziehau 	if (error) {
18369c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "bringing up %s failed: %d\n",
18374db5958aSJustin Hibbits 		    if_name(sc->hn_vf_ifp), error);
18389c6cae24SSepherosa Ziehau 		return;
18399c6cae24SSepherosa Ziehau 	}
18409c6cae24SSepherosa Ziehau 
18419c6cae24SSepherosa Ziehau 	/*
18429c6cae24SSepherosa Ziehau 	 * NOTE:
18439c6cae24SSepherosa Ziehau 	 * Datapath setting must happen _after_ bringing the VF up.
18449c6cae24SSepherosa Ziehau 	 */
18459c6cae24SSepherosa Ziehau 	hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
18469c6cae24SSepherosa Ziehau 
1847642ec226SSepherosa Ziehau 	/*
1848642ec226SSepherosa Ziehau 	 * NOTE:
1849642ec226SSepherosa Ziehau 	 * Fixup RSS related bits _after_ the VF is brought up, since
1850642ec226SSepherosa Ziehau 	 * many VFs generate RSS key during it's initialization.
1851642ec226SSepherosa Ziehau 	 */
1852642ec226SSepherosa Ziehau 	hn_vf_rss_fixup(sc, true);
1853642ec226SSepherosa Ziehau 
1854a97fff19SSepherosa Ziehau 	/* Mark transparent mode VF as enabled. */
1855a97fff19SSepherosa Ziehau 	hn_xpnt_vf_setenable(sc);
18569c6cae24SSepherosa Ziehau }
18579c6cae24SSepherosa Ziehau 
18589c6cae24SSepherosa Ziehau static void
18599c6cae24SSepherosa Ziehau hn_xpnt_vf_init_taskfunc(void *xsc, int pending __unused)
18609c6cae24SSepherosa Ziehau {
18619c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
18629c6cae24SSepherosa Ziehau 
18639c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
18649c6cae24SSepherosa Ziehau 
18659c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
18669c6cae24SSepherosa Ziehau 		goto done;
18679c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
18689c6cae24SSepherosa Ziehau 		goto done;
18699c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
18709c6cae24SSepherosa Ziehau 		goto done;
18719c6cae24SSepherosa Ziehau 
18729c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick != 0) {
18739c6cae24SSepherosa Ziehau 		/* Mark VF as ready. */
18749c6cae24SSepherosa Ziehau 		hn_xpnt_vf_setready(sc);
18759c6cae24SSepherosa Ziehau 	}
18769c6cae24SSepherosa Ziehau 
18774db5958aSJustin Hibbits 	if (if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) {
18789c6cae24SSepherosa Ziehau 		/*
18799c6cae24SSepherosa Ziehau 		 * Delayed VF initialization.
18809c6cae24SSepherosa Ziehau 		 */
18819c6cae24SSepherosa Ziehau 		if (bootverbose) {
18829c6cae24SSepherosa Ziehau 			if_printf(sc->hn_ifp, "delayed initialize %s\n",
18834db5958aSJustin Hibbits 			    if_name(sc->hn_vf_ifp));
18849c6cae24SSepherosa Ziehau 		}
18859c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
18869c6cae24SSepherosa Ziehau 	}
18879c6cae24SSepherosa Ziehau done:
18889c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
18899c6cae24SSepherosa Ziehau }
18909c6cae24SSepherosa Ziehau 
1891499c3e17SSepherosa Ziehau static void
18924db5958aSJustin Hibbits hn_ifnet_attevent(void *xsc, if_t ifp)
1893499c3e17SSepherosa Ziehau {
1894499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1895499c3e17SSepherosa Ziehau 
1896499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1897499c3e17SSepherosa Ziehau 
1898499c3e17SSepherosa Ziehau 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
1899499c3e17SSepherosa Ziehau 		goto done;
1900499c3e17SSepherosa Ziehau 
1901499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1902499c3e17SSepherosa Ziehau 		goto done;
1903499c3e17SSepherosa Ziehau 
1904499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp != NULL) {
1905499c3e17SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s was attached as VF\n",
19064db5958aSJustin Hibbits 		    if_name(sc->hn_vf_ifp));
1907499c3e17SSepherosa Ziehau 		goto done;
1908499c3e17SSepherosa Ziehau 	}
1909499c3e17SSepherosa Ziehau 
19104db5958aSJustin Hibbits 	if (hn_xpnt_vf && if_getstartfn(ifp) != NULL) {
19119c6cae24SSepherosa Ziehau 		/*
19129c6cae24SSepherosa Ziehau 		 * ifnet.if_start is _not_ supported by transparent
19139c6cae24SSepherosa Ziehau 		 * mode VF; mainly due to the IFF_DRV_OACTIVE flag.
19149c6cae24SSepherosa Ziehau 		 */
19159c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s uses if_start, which is unsupported "
19164db5958aSJustin Hibbits 		    "in transparent VF mode.\n", if_name(sc->hn_vf_ifp));
19174db5958aSJustin Hibbits 
19189c6cae24SSepherosa Ziehau 		goto done;
19199c6cae24SSepherosa Ziehau 	}
19209c6cae24SSepherosa Ziehau 
1921499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
1922499c3e17SSepherosa Ziehau 
19234db5958aSJustin Hibbits 	if (if_getindex(ifp) >= hn_vfmap_size) {
19244db5958aSJustin Hibbits 		if_t *newmap;
1925499c3e17SSepherosa Ziehau 		int newsize;
1926499c3e17SSepherosa Ziehau 
19274db5958aSJustin Hibbits 		newsize = if_getindex(ifp) + HN_VFMAP_SIZE_DEF;
19284db5958aSJustin Hibbits 		newmap = malloc(sizeof(if_t) * newsize, M_DEVBUF,
1929499c3e17SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
1930499c3e17SSepherosa Ziehau 
1931499c3e17SSepherosa Ziehau 		memcpy(newmap, hn_vfmap,
19324db5958aSJustin Hibbits 		    sizeof(if_t) * hn_vfmap_size);
1933499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
1934499c3e17SSepherosa Ziehau 		hn_vfmap = newmap;
1935499c3e17SSepherosa Ziehau 		hn_vfmap_size = newsize;
1936499c3e17SSepherosa Ziehau 	}
19374db5958aSJustin Hibbits 	KASSERT(hn_vfmap[if_getindex(ifp)] == NULL,
1938499c3e17SSepherosa Ziehau 	    ("%s: ifindex %d was mapped to %s",
19394db5958aSJustin Hibbits 	     if_name(ifp), if_getindex(ifp), if_name(hn_vfmap[if_getindex(ifp)])));
19404db5958aSJustin Hibbits 	hn_vfmap[if_getindex(ifp)] = sc->hn_ifp;
1941499c3e17SSepherosa Ziehau 
1942499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
1943499c3e17SSepherosa Ziehau 
19449c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
19459c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
19469c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
19474db5958aSJustin Hibbits 	    ("%s: transparent VF was enabled", if_name(sc->hn_ifp)));
1948499c3e17SSepherosa Ziehau 	sc->hn_vf_ifp = ifp;
19499c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
19509c6cae24SSepherosa Ziehau 
19519c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
19529c6cae24SSepherosa Ziehau 		int wait_ticks;
19539c6cae24SSepherosa Ziehau 
19549c6cae24SSepherosa Ziehau 		/*
19559c6cae24SSepherosa Ziehau 		 * Install if_input for vf_ifp, which does vf_ifp -> hn_ifp.
19569c6cae24SSepherosa Ziehau 		 * Save vf_ifp's current if_input for later restoration.
19579c6cae24SSepherosa Ziehau 		 */
19584db5958aSJustin Hibbits 		sc->hn_vf_input = if_getinputfn(ifp);
19594db5958aSJustin Hibbits 		if_setinputfn(ifp, hn_xpnt_vf_input);
19609c6cae24SSepherosa Ziehau 
19619c6cae24SSepherosa Ziehau 		/*
19629c6cae24SSepherosa Ziehau 		 * Stop link status management; use the VF's.
19639c6cae24SSepherosa Ziehau 		 */
19649c6cae24SSepherosa Ziehau 		hn_suspend_mgmt(sc);
19659c6cae24SSepherosa Ziehau 
19669c6cae24SSepherosa Ziehau 		/*
19679c6cae24SSepherosa Ziehau 		 * Give VF sometime to complete its attach routing.
19689c6cae24SSepherosa Ziehau 		 */
19699c6cae24SSepherosa Ziehau 		wait_ticks = hn_xpnt_vf_attwait * hz;
19709c6cae24SSepherosa Ziehau 		sc->hn_vf_rdytick = ticks + wait_ticks;
19719c6cae24SSepherosa Ziehau 
19729c6cae24SSepherosa Ziehau 		taskqueue_enqueue_timeout(sc->hn_vf_taskq, &sc->hn_vf_init,
19739c6cae24SSepherosa Ziehau 		    wait_ticks);
19749c6cae24SSepherosa Ziehau 	}
1975499c3e17SSepherosa Ziehau done:
1976499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
1977499c3e17SSepherosa Ziehau }
1978499c3e17SSepherosa Ziehau 
1979499c3e17SSepherosa Ziehau static void
19804db5958aSJustin Hibbits hn_ifnet_detevent(void *xsc, if_t ifp)
1981499c3e17SSepherosa Ziehau {
1982499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1983499c3e17SSepherosa Ziehau 
1984499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1985499c3e17SSepherosa Ziehau 
1986499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
1987499c3e17SSepherosa Ziehau 		goto done;
1988499c3e17SSepherosa Ziehau 
1989499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1990499c3e17SSepherosa Ziehau 		goto done;
1991499c3e17SSepherosa Ziehau 
19929c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
19939c6cae24SSepherosa Ziehau 		/*
19949c6cae24SSepherosa Ziehau 		 * Make sure that the delayed initialization is not running.
19959c6cae24SSepherosa Ziehau 		 *
19969c6cae24SSepherosa Ziehau 		 * NOTE:
19979c6cae24SSepherosa Ziehau 		 * - This lock _must_ be released, since the hn_vf_init task
19989c6cae24SSepherosa Ziehau 		 *   will try holding this lock.
19999c6cae24SSepherosa Ziehau 		 * - It is safe to release this lock here, since the
20009c6cae24SSepherosa Ziehau 		 *   hn_ifnet_attevent() is interlocked by the hn_vf_ifp.
20019c6cae24SSepherosa Ziehau 		 *
20029c6cae24SSepherosa Ziehau 		 * XXX racy, if hn(4) ever detached.
20039c6cae24SSepherosa Ziehau 		 */
20049c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
20059c6cae24SSepherosa Ziehau 		taskqueue_drain_timeout(sc->hn_vf_taskq, &sc->hn_vf_init);
20069c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
20079c6cae24SSepherosa Ziehau 
20089c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_input != NULL, ("%s VF input is not saved",
20094db5958aSJustin Hibbits 		    if_name(sc->hn_ifp)));
20104db5958aSJustin Hibbits 		if_setinputfn(ifp, sc->hn_vf_input);
20119c6cae24SSepherosa Ziehau 		sc->hn_vf_input = NULL;
20129c6cae24SSepherosa Ziehau 
2013642ec226SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) &&
2014642ec226SSepherosa Ziehau 		    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED))
20159c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
20169c6cae24SSepherosa Ziehau 
20179c6cae24SSepherosa Ziehau 		if (sc->hn_vf_rdytick == 0) {
20189c6cae24SSepherosa Ziehau 			/*
20199c6cae24SSepherosa Ziehau 			 * The VF was ready; restore some settings.
20209c6cae24SSepherosa Ziehau 			 */
20214db5958aSJustin Hibbits 			if_setcapabilities(ifp, sc->hn_saved_caps);
20229c6cae24SSepherosa Ziehau 			/*
20239c6cae24SSepherosa Ziehau 			 * NOTE:
20249c6cae24SSepherosa Ziehau 			 * There is _no_ need to fixup if_capenable and
20259c6cae24SSepherosa Ziehau 			 * if_hwassist, since the if_capabilities before
20269c6cae24SSepherosa Ziehau 			 * restoration was an intersection of the VF's
20279c6cae24SSepherosa Ziehau 			 * if_capabilites and the synthetic device's
20289c6cae24SSepherosa Ziehau 			 * if_capabilites.
20299c6cae24SSepherosa Ziehau 			 */
20304db5958aSJustin Hibbits 			if_sethwtsomax(ifp, sc->hn_saved_tsomax);
20314db5958aSJustin Hibbits 			if_sethwtsomaxsegcount(sc->hn_ifp,
20324db5958aSJustin Hibbits 			    sc->hn_saved_tsosegcnt);
20334db5958aSJustin Hibbits 			if_sethwtsomaxsegsize(ifp, sc->hn_saved_tsosegsz);
20349c6cae24SSepherosa Ziehau 		}
20359c6cae24SSepherosa Ziehau 
2036642ec226SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
2037642ec226SSepherosa Ziehau 			/*
2038642ec226SSepherosa Ziehau 			 * Restore RSS settings.
2039642ec226SSepherosa Ziehau 			 */
2040642ec226SSepherosa Ziehau 			hn_vf_rss_restore(sc);
2041642ec226SSepherosa Ziehau 
20429c6cae24SSepherosa Ziehau 			/*
20439c6cae24SSepherosa Ziehau 			 * Resume link status management, which was suspended
20449c6cae24SSepherosa Ziehau 			 * by hn_ifnet_attevent().
20459c6cae24SSepherosa Ziehau 			 */
20469c6cae24SSepherosa Ziehau 			hn_resume_mgmt(sc);
20479c6cae24SSepherosa Ziehau 		}
2048642ec226SSepherosa Ziehau 	}
20499c6cae24SSepherosa Ziehau 
2050a97fff19SSepherosa Ziehau 	/* Mark transparent mode VF as disabled. */
2051a97fff19SSepherosa Ziehau 	hn_xpnt_vf_setdisable(sc, true /* clear hn_vf_ifp */);
2052499c3e17SSepherosa Ziehau 
2053499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
2054499c3e17SSepherosa Ziehau 
20554db5958aSJustin Hibbits 	KASSERT(if_getindex(ifp) < hn_vfmap_size,
20564db5958aSJustin Hibbits 	    ("ifindex %d, vfmapsize %d", if_getindex(ifp), hn_vfmap_size));
20574db5958aSJustin Hibbits 	if (hn_vfmap[if_getindex(ifp)] != NULL) {
20584db5958aSJustin Hibbits 		KASSERT(hn_vfmap[if_getindex(ifp)] == sc->hn_ifp,
2059499c3e17SSepherosa Ziehau 		    ("%s: ifindex %d was mapped to %s",
20604db5958aSJustin Hibbits 		     if_name(ifp), if_getindex(ifp),
20614db5958aSJustin Hibbits 		     if_name(hn_vfmap[if_getindex(ifp)])));
20624db5958aSJustin Hibbits 		hn_vfmap[if_getindex(ifp)] = NULL;
2063499c3e17SSepherosa Ziehau 	}
2064499c3e17SSepherosa Ziehau 
2065499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
2066499c3e17SSepherosa Ziehau done:
2067499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
2068499c3e17SSepherosa Ziehau }
2069499c3e17SSepherosa Ziehau 
20709c6cae24SSepherosa Ziehau static void
20714db5958aSJustin Hibbits hn_ifnet_lnkevent(void *xsc, if_t ifp, int link_state)
20729c6cae24SSepherosa Ziehau {
20739c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
20749c6cae24SSepherosa Ziehau 
20759c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == ifp)
20769c6cae24SSepherosa Ziehau 		if_link_state_change(sc->hn_ifp, link_state);
20779c6cae24SSepherosa Ziehau }
20789c6cae24SSepherosa Ziehau 
207915516c77SSepherosa Ziehau static int
20804db5958aSJustin Hibbits hn_tsomax_sysctl(SYSCTL_HANDLER_ARGS)
20814db5958aSJustin Hibbits {
20824db5958aSJustin Hibbits 	struct hn_softc *sc = arg1;
20834db5958aSJustin Hibbits 	unsigned int tsomax;
20844db5958aSJustin Hibbits 	int error;
20854db5958aSJustin Hibbits 
20864db5958aSJustin Hibbits 	tsomax = if_gethwtsomax(sc->hn_ifp);
20874db5958aSJustin Hibbits 	error = sysctl_handle_int(oidp, &tsomax, 0, req);
20884db5958aSJustin Hibbits 	return error;
20894db5958aSJustin Hibbits }
20904db5958aSJustin Hibbits 
20914db5958aSJustin Hibbits static int
20924db5958aSJustin Hibbits hn_tsomaxsegcnt_sysctl(SYSCTL_HANDLER_ARGS)
20934db5958aSJustin Hibbits {
20944db5958aSJustin Hibbits 	struct hn_softc *sc = arg1;
20954db5958aSJustin Hibbits 	unsigned int tsomaxsegcnt;
20964db5958aSJustin Hibbits 	int error;
20974db5958aSJustin Hibbits 
20984db5958aSJustin Hibbits 	tsomaxsegcnt = if_gethwtsomaxsegcount(sc->hn_ifp);
20994db5958aSJustin Hibbits 	error = sysctl_handle_int(oidp, &tsomaxsegcnt, 0, req);
21004db5958aSJustin Hibbits 	return error;
21014db5958aSJustin Hibbits }
21024db5958aSJustin Hibbits 
21034db5958aSJustin Hibbits static int
21044db5958aSJustin Hibbits hn_tsomaxsegsz_sysctl(SYSCTL_HANDLER_ARGS)
21054db5958aSJustin Hibbits {
21064db5958aSJustin Hibbits 	struct hn_softc *sc = arg1;
21074db5958aSJustin Hibbits 	unsigned int tsomaxsegsz;
21084db5958aSJustin Hibbits 	int error;
21094db5958aSJustin Hibbits 
21104db5958aSJustin Hibbits 	tsomaxsegsz = if_gethwtsomaxsegsize(sc->hn_ifp);
21114db5958aSJustin Hibbits 	error = sysctl_handle_int(oidp, &tsomaxsegsz, 0, req);
21124db5958aSJustin Hibbits 	return error;
21134db5958aSJustin Hibbits }
21144db5958aSJustin Hibbits 
21154db5958aSJustin Hibbits static int
211615516c77SSepherosa Ziehau hn_probe(device_t dev)
211715516c77SSepherosa Ziehau {
211815516c77SSepherosa Ziehau 
2119c2d50b26SSepherosa Ziehau 	if (VMBUS_PROBE_GUID(device_get_parent(dev), dev, &hn_guid) == 0) {
212015516c77SSepherosa Ziehau 		device_set_desc(dev, "Hyper-V Network Interface");
212115516c77SSepherosa Ziehau 		return BUS_PROBE_DEFAULT;
212215516c77SSepherosa Ziehau 	}
212315516c77SSepherosa Ziehau 	return ENXIO;
212415516c77SSepherosa Ziehau }
212515516c77SSepherosa Ziehau 
212615516c77SSepherosa Ziehau static int
212715516c77SSepherosa Ziehau hn_attach(device_t dev)
212815516c77SSepherosa Ziehau {
212915516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
213015516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
213115516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
213215516c77SSepherosa Ziehau 	uint8_t eaddr[ETHER_ADDR_LEN];
21334db5958aSJustin Hibbits 	if_t ifp = NULL;
213415516c77SSepherosa Ziehau 	int error, ring_cnt, tx_ring_cnt;
2135eb2fe044SSepherosa Ziehau 	uint32_t mtu;
213615516c77SSepherosa Ziehau 
213715516c77SSepherosa Ziehau 	sc->hn_dev = dev;
213815516c77SSepherosa Ziehau 	sc->hn_prichan = vmbus_get_channel(dev);
213915516c77SSepherosa Ziehau 	HN_LOCK_INIT(sc);
21409c6cae24SSepherosa Ziehau 	rm_init(&sc->hn_vf_lock, "hnvf");
21419c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_xpnt_vf_accbpf)
21429c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
214315516c77SSepherosa Ziehau 
214415516c77SSepherosa Ziehau 	/*
2145dc13fee6SSepherosa Ziehau 	 * Initialize these tunables once.
2146dc13fee6SSepherosa Ziehau 	 */
2147dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = hn_tx_agg_size;
2148dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = hn_tx_agg_pkts;
2149dc13fee6SSepherosa Ziehau 
2150dc13fee6SSepherosa Ziehau 	/*
215115516c77SSepherosa Ziehau 	 * Setup taskqueue for transmission.
215215516c77SSepherosa Ziehau 	 */
21530e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_INDEP) {
2154fdd0222aSSepherosa Ziehau 		int i;
2155fdd0222aSSepherosa Ziehau 
2156fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs =
2157fdd0222aSSepherosa Ziehau 		    malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
2158fdd0222aSSepherosa Ziehau 		    M_DEVBUF, M_WAITOK);
2159fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i) {
2160fdd0222aSSepherosa Ziehau 			sc->hn_tx_taskqs[i] = taskqueue_create("hn_tx",
2161fdd0222aSSepherosa Ziehau 			    M_WAITOK, taskqueue_thread_enqueue,
2162fdd0222aSSepherosa Ziehau 			    &sc->hn_tx_taskqs[i]);
2163fdd0222aSSepherosa Ziehau 			taskqueue_start_threads(&sc->hn_tx_taskqs[i], 1, PI_NET,
2164fdd0222aSSepherosa Ziehau 			    "%s tx%d", device_get_nameunit(dev), i);
2165fdd0222aSSepherosa Ziehau 		}
21660e11868dSSepherosa Ziehau 	} else if (hn_tx_taskq_mode == HN_TX_TASKQ_M_GLOBAL) {
2167fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs = hn_tx_taskque;
216815516c77SSepherosa Ziehau 	}
216915516c77SSepherosa Ziehau 
217015516c77SSepherosa Ziehau 	/*
217115516c77SSepherosa Ziehau 	 * Setup taskqueue for mangement tasks, e.g. link status.
217215516c77SSepherosa Ziehau 	 */
217315516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq0 = taskqueue_create("hn_mgmt", M_WAITOK,
217415516c77SSepherosa Ziehau 	    taskqueue_thread_enqueue, &sc->hn_mgmt_taskq0);
217515516c77SSepherosa Ziehau 	taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
217615516c77SSepherosa Ziehau 	    device_get_nameunit(dev));
217715516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc);
217815516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_netchg_init, 0, hn_netchg_init_taskfunc, sc);
217915516c77SSepherosa Ziehau 	TIMEOUT_TASK_INIT(sc->hn_mgmt_taskq0, &sc->hn_netchg_status, 0,
218015516c77SSepherosa Ziehau 	    hn_netchg_status_taskfunc, sc);
218115516c77SSepherosa Ziehau 
21829c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
21839c6cae24SSepherosa Ziehau 		/*
21849c6cae24SSepherosa Ziehau 		 * Setup taskqueue for VF tasks, e.g. delayed VF bringing up.
21859c6cae24SSepherosa Ziehau 		 */
21869c6cae24SSepherosa Ziehau 		sc->hn_vf_taskq = taskqueue_create("hn_vf", M_WAITOK,
21879c6cae24SSepherosa Ziehau 		    taskqueue_thread_enqueue, &sc->hn_vf_taskq);
21889c6cae24SSepherosa Ziehau 		taskqueue_start_threads(&sc->hn_vf_taskq, 1, PI_NET, "%s vf",
21899c6cae24SSepherosa Ziehau 		    device_get_nameunit(dev));
21909c6cae24SSepherosa Ziehau 		TIMEOUT_TASK_INIT(sc->hn_vf_taskq, &sc->hn_vf_init, 0,
21919c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_init_taskfunc, sc);
21929c6cae24SSepherosa Ziehau 	}
21939c6cae24SSepherosa Ziehau 
219415516c77SSepherosa Ziehau 	/*
219515516c77SSepherosa Ziehau 	 * Allocate ifnet and setup its name earlier, so that if_printf
219615516c77SSepherosa Ziehau 	 * can be used by functions, which will be called after
219715516c77SSepherosa Ziehau 	 * ether_ifattach().
219815516c77SSepherosa Ziehau 	 */
219915516c77SSepherosa Ziehau 	ifp = sc->hn_ifp = if_alloc(IFT_ETHER);
22004db5958aSJustin Hibbits 	if_setsoftc(ifp, sc);
220115516c77SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
220215516c77SSepherosa Ziehau 
220315516c77SSepherosa Ziehau 	/*
220415516c77SSepherosa Ziehau 	 * Initialize ifmedia earlier so that it can be unconditionally
220515516c77SSepherosa Ziehau 	 * destroyed, if error happened later on.
220615516c77SSepherosa Ziehau 	 */
220715516c77SSepherosa Ziehau 	ifmedia_init(&sc->hn_media, 0, hn_ifmedia_upd, hn_ifmedia_sts);
220815516c77SSepherosa Ziehau 
220915516c77SSepherosa Ziehau 	/*
221015516c77SSepherosa Ziehau 	 * Figure out the # of RX rings (ring_cnt) and the # of TX rings
221115516c77SSepherosa Ziehau 	 * to use (tx_ring_cnt).
221215516c77SSepherosa Ziehau 	 *
221315516c77SSepherosa Ziehau 	 * NOTE:
221415516c77SSepherosa Ziehau 	 * The # of RX rings to use is same as the # of channels to use.
221515516c77SSepherosa Ziehau 	 */
221615516c77SSepherosa Ziehau 	ring_cnt = hn_chan_cnt;
221715516c77SSepherosa Ziehau 	if (ring_cnt <= 0) {
221815516c77SSepherosa Ziehau 		/* Default */
221915516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
222015516c77SSepherosa Ziehau 		if (ring_cnt > HN_RING_CNT_DEF_MAX)
222115516c77SSepherosa Ziehau 			ring_cnt = HN_RING_CNT_DEF_MAX;
222215516c77SSepherosa Ziehau 	} else if (ring_cnt > mp_ncpus) {
222315516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
222415516c77SSepherosa Ziehau 	}
222534d68912SSepherosa Ziehau #ifdef RSS
222634d68912SSepherosa Ziehau 	if (ring_cnt > rss_getnumbuckets())
222734d68912SSepherosa Ziehau 		ring_cnt = rss_getnumbuckets();
222834d68912SSepherosa Ziehau #endif
222915516c77SSepherosa Ziehau 
223015516c77SSepherosa Ziehau 	tx_ring_cnt = hn_tx_ring_cnt;
223115516c77SSepherosa Ziehau 	if (tx_ring_cnt <= 0 || tx_ring_cnt > ring_cnt)
223215516c77SSepherosa Ziehau 		tx_ring_cnt = ring_cnt;
223323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
223415516c77SSepherosa Ziehau 	if (hn_use_if_start) {
223515516c77SSepherosa Ziehau 		/* ifnet.if_start only needs one TX ring. */
223615516c77SSepherosa Ziehau 		tx_ring_cnt = 1;
223715516c77SSepherosa Ziehau 	}
223823bf9e15SSepherosa Ziehau #endif
223915516c77SSepherosa Ziehau 
224015516c77SSepherosa Ziehau 	/*
224115516c77SSepherosa Ziehau 	 * Set the leader CPU for channels.
224215516c77SSepherosa Ziehau 	 */
224315516c77SSepherosa Ziehau 	sc->hn_cpu = atomic_fetchadd_int(&hn_cpu_index, ring_cnt) % mp_ncpus;
224415516c77SSepherosa Ziehau 
224515516c77SSepherosa Ziehau 	/*
224615516c77SSepherosa Ziehau 	 * Create enough TX/RX rings, even if only limited number of
224715516c77SSepherosa Ziehau 	 * channels can be allocated.
224815516c77SSepherosa Ziehau 	 */
224915516c77SSepherosa Ziehau 	error = hn_create_tx_data(sc, tx_ring_cnt);
225015516c77SSepherosa Ziehau 	if (error)
225115516c77SSepherosa Ziehau 		goto failed;
225215516c77SSepherosa Ziehau 	error = hn_create_rx_data(sc, ring_cnt);
225315516c77SSepherosa Ziehau 	if (error)
225415516c77SSepherosa Ziehau 		goto failed;
225515516c77SSepherosa Ziehau 
225615516c77SSepherosa Ziehau 	/*
225715516c77SSepherosa Ziehau 	 * Create transaction context for NVS and RNDIS transactions.
225815516c77SSepherosa Ziehau 	 */
225915516c77SSepherosa Ziehau 	sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
226015516c77SSepherosa Ziehau 	    HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
226125641fc7SSepherosa Ziehau 	if (sc->hn_xact == NULL) {
226225641fc7SSepherosa Ziehau 		error = ENXIO;
226315516c77SSepherosa Ziehau 		goto failed;
226425641fc7SSepherosa Ziehau 	}
226525641fc7SSepherosa Ziehau 
226625641fc7SSepherosa Ziehau 	/*
226725641fc7SSepherosa Ziehau 	 * Install orphan handler for the revocation of this device's
226825641fc7SSepherosa Ziehau 	 * primary channel.
226925641fc7SSepherosa Ziehau 	 *
227025641fc7SSepherosa Ziehau 	 * NOTE:
227125641fc7SSepherosa Ziehau 	 * The processing order is critical here:
227225641fc7SSepherosa Ziehau 	 * Install the orphan handler, _before_ testing whether this
227325641fc7SSepherosa Ziehau 	 * device's primary channel has been revoked or not.
227425641fc7SSepherosa Ziehau 	 */
227525641fc7SSepherosa Ziehau 	vmbus_chan_set_orphan(sc->hn_prichan, sc->hn_xact);
227625641fc7SSepherosa Ziehau 	if (vmbus_chan_is_revoked(sc->hn_prichan)) {
227725641fc7SSepherosa Ziehau 		error = ENXIO;
227825641fc7SSepherosa Ziehau 		goto failed;
227925641fc7SSepherosa Ziehau 	}
228015516c77SSepherosa Ziehau 
228115516c77SSepherosa Ziehau 	/*
228215516c77SSepherosa Ziehau 	 * Attach the synthetic parts, i.e. NVS and RNDIS.
228315516c77SSepherosa Ziehau 	 */
228415516c77SSepherosa Ziehau 	error = hn_synth_attach(sc, ETHERMTU);
228515516c77SSepherosa Ziehau 	if (error)
228615516c77SSepherosa Ziehau 		goto failed;
228715516c77SSepherosa Ziehau 
228815516c77SSepherosa Ziehau 	error = hn_rndis_get_eaddr(sc, eaddr);
228915516c77SSepherosa Ziehau 	if (error)
229015516c77SSepherosa Ziehau 		goto failed;
229115516c77SSepherosa Ziehau 
2292eb2fe044SSepherosa Ziehau 	error = hn_rndis_get_mtu(sc, &mtu);
2293eb2fe044SSepherosa Ziehau 	if (error)
2294eb2fe044SSepherosa Ziehau 		mtu = ETHERMTU;
2295eb2fe044SSepherosa Ziehau 	else if (bootverbose)
2296eb2fe044SSepherosa Ziehau 		device_printf(dev, "RNDIS mtu %u\n", mtu);
2297eb2fe044SSepherosa Ziehau 
229815516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
229915516c77SSepherosa Ziehau 		/*
230015516c77SSepherosa Ziehau 		 * Reduce TCP segment aggregation limit for multiple
230115516c77SSepherosa Ziehau 		 * RX rings to increase ACK timeliness.
230215516c77SSepherosa Ziehau 		 */
230315516c77SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF);
230415516c77SSepherosa Ziehau 	}
230515516c77SSepherosa Ziehau 
230615516c77SSepherosa Ziehau 	/*
2307db76829bSSepherosa Ziehau 	 * Fixup TX/RX stuffs after synthetic parts are attached.
230815516c77SSepherosa Ziehau 	 */
230915516c77SSepherosa Ziehau 	hn_fixup_tx_data(sc);
2310db76829bSSepherosa Ziehau 	hn_fixup_rx_data(sc);
231115516c77SSepherosa Ziehau 
231215516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
231315516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
231415516c77SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "nvs_version", CTLFLAG_RD,
231515516c77SSepherosa Ziehau 	    &sc->hn_nvs_ver, 0, "NVS version");
231615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ndis_version",
231715516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
231815516c77SSepherosa Ziehau 	    hn_ndis_version_sysctl, "A", "NDIS version");
231915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "caps",
232015516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
232115516c77SSepherosa Ziehau 	    hn_caps_sysctl, "A", "capabilities");
232215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist",
232315516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
232415516c77SSepherosa Ziehau 	    hn_hwassist_sysctl, "A", "hwassist");
23254db5958aSJustin Hibbits 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tso_max",
23264db5958aSJustin Hibbits 	    CTLTYPE_UINT | CTLFLAG_RD, sc, 0, hn_tsomax_sysctl,
23274db5958aSJustin Hibbits 	    "IU", "max TSO size");
23284db5958aSJustin Hibbits 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tso_maxsegcnt",
23294db5958aSJustin Hibbits 	    CTLTYPE_UINT | CTLFLAG_RD, sc, 0, hn_tsomaxsegcnt_sysctl,
23304db5958aSJustin Hibbits 	    "IU", "max # of TSO segments");
23314db5958aSJustin Hibbits 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tso_maxsegsz",
23324db5958aSJustin Hibbits 	    CTLTYPE_UINT | CTLFLAG_RD, sc, 0, hn_tsomaxsegsz_sysctl,
23334db5958aSJustin Hibbits 	    "IU", "max size of TSO segment");
233415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter",
233515516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
233615516c77SSepherosa Ziehau 	    hn_rxfilter_sysctl, "A", "rxfilter");
233715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hash",
233815516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
233915516c77SSepherosa Ziehau 	    hn_rss_hash_sysctl, "A", "RSS hash");
2340642ec226SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hashcap",
2341642ec226SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2342642ec226SSepherosa Ziehau 	    hn_rss_hcap_sysctl, "A", "RSS hash capabilities");
2343642ec226SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "mbuf_hash",
2344642ec226SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2345642ec226SSepherosa Ziehau 	    hn_rss_mbuf_sysctl, "A", "RSS hash for mbufs");
234615516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rss_ind_size",
234715516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rss_ind_size, 0, "RSS indirect entry count");
234834d68912SSepherosa Ziehau #ifndef RSS
234934d68912SSepherosa Ziehau 	/*
235034d68912SSepherosa Ziehau 	 * Don't allow RSS key/indirect table changes, if RSS is defined.
235134d68912SSepherosa Ziehau 	 */
235215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key",
235315516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
235415516c77SSepherosa Ziehau 	    hn_rss_key_sysctl, "IU", "RSS key");
235515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_ind",
235615516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
235715516c77SSepherosa Ziehau 	    hn_rss_ind_sysctl, "IU", "RSS indirect table");
235834d68912SSepherosa Ziehau #endif
2359dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_size",
2360dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_size, 0,
2361dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation size limit");
2362dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_pkts",
2363dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_pkts, 0,
2364dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation count limit");
2365dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_align",
2366dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_align, 0,
2367dc13fee6SSepherosa Ziehau 	    "RNDIS packet transmission aggregation alignment");
2368dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_size",
2369dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
2370dc13fee6SSepherosa Ziehau 	    hn_txagg_size_sysctl, "I",
2371dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation size, 0 -- disable, -1 -- auto");
2372dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pkts",
2373dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
2374dc13fee6SSepherosa Ziehau 	    hn_txagg_pkts_sysctl, "I",
2375dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation packets, "
2376dc13fee6SSepherosa Ziehau 	    "0 -- disable, -1 -- auto");
23776c1204dfSSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "polling",
23786c1204dfSSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
23796c1204dfSSepherosa Ziehau 	    hn_polling_sysctl, "I",
23806c1204dfSSepherosa Ziehau 	    "Polling frequency: [100,1000000], 0 disable polling");
238140d60d6eSDexuan Cui 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf",
238240d60d6eSDexuan Cui 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
238340d60d6eSDexuan Cui 	    hn_vf_sysctl, "A", "Virtual Function's name");
23849c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
2385499c3e17SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxvf",
2386499c3e17SSepherosa Ziehau 		    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2387499c3e17SSepherosa Ziehau 		    hn_rxvf_sysctl, "A", "activated Virtual Function's name");
23889c6cae24SSepherosa Ziehau 	} else {
23899c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_enabled",
23909c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
23919c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_enabled_sysctl, "I",
23929c6cae24SSepherosa Ziehau 		    "Transparent VF enabled");
23939c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_accbpf",
23949c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
23959c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_accbpf_sysctl, "I",
23969c6cae24SSepherosa Ziehau 		    "Accurate BPF for transparent VF");
23979c6cae24SSepherosa Ziehau 	}
239815516c77SSepherosa Ziehau 
239980c3eb7bSWei Hu 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rsc_switch",
240080c3eb7bSWei Hu 	    CTLTYPE_UINT | CTLFLAG_RW, sc, 0, hn_rsc_sysctl, "A",
240180c3eb7bSWei Hu 	    "switch to rsc");
240280c3eb7bSWei Hu 
240315516c77SSepherosa Ziehau 	/*
240415516c77SSepherosa Ziehau 	 * Setup the ifmedia, which has been initialized earlier.
240515516c77SSepherosa Ziehau 	 */
240615516c77SSepherosa Ziehau 	ifmedia_add(&sc->hn_media, IFM_ETHER | IFM_AUTO, 0, NULL);
240715516c77SSepherosa Ziehau 	ifmedia_set(&sc->hn_media, IFM_ETHER | IFM_AUTO);
240815516c77SSepherosa Ziehau 	/* XXX ifmedia_set really should do this for us */
240915516c77SSepherosa Ziehau 	sc->hn_media.ifm_media = sc->hn_media.ifm_cur->ifm_media;
241015516c77SSepherosa Ziehau 
241115516c77SSepherosa Ziehau 	/*
241215516c77SSepherosa Ziehau 	 * Setup the ifnet for this interface.
241315516c77SSepherosa Ziehau 	 */
241415516c77SSepherosa Ziehau 
24154db5958aSJustin Hibbits 	if_setbaudrate(ifp, IF_Gbps(10));
24164db5958aSJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
24174db5958aSJustin Hibbits 	if_setioctlfn(ifp, hn_ioctl);
24184db5958aSJustin Hibbits 	if_setinitfn(ifp, hn_init);
241923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
242015516c77SSepherosa Ziehau 	if (hn_use_if_start) {
242115516c77SSepherosa Ziehau 		int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]);
242215516c77SSepherosa Ziehau 
24234db5958aSJustin Hibbits 		if_setstartfn(ifp, hn_start);
24244db5958aSJustin Hibbits 		if_setsendqlen(ifp, qdepth);
24254db5958aSJustin Hibbits 		if_setsendqready(ifp);
242623bf9e15SSepherosa Ziehau 	} else
242723bf9e15SSepherosa Ziehau #endif
242823bf9e15SSepherosa Ziehau 	{
24294db5958aSJustin Hibbits 		if_settransmitfn(ifp, hn_transmit);
24304db5958aSJustin Hibbits 		if_setqflushfn(ifp, hn_xmit_qflush);
243115516c77SSepherosa Ziehau 	}
243215516c77SSepherosa Ziehau 
24334db5958aSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_RXCSUM | IFCAP_LRO | IFCAP_LINKSTATE, 0);
243415516c77SSepherosa Ziehau #ifdef foo
243515516c77SSepherosa Ziehau 	/* We can't diff IPv6 packets from IPv4 packets on RX path. */
24364db5958aSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_RXCSUM_IPV6, 0);
243715516c77SSepherosa Ziehau #endif
243815516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_VLAN) {
243915516c77SSepherosa Ziehau 		/* XXX not sure about VLAN_MTU. */
24404db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU, 0);
244115516c77SSepherosa Ziehau 	}
244215516c77SSepherosa Ziehau 
24434db5958aSJustin Hibbits 	if_sethwassist(ifp, sc->hn_tx_ring[0].hn_csum_assist);
24444db5958aSJustin Hibbits 	if (if_gethwassist(ifp) & HN_CSUM_IP_MASK)
24454db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_TXCSUM, 0);
24464db5958aSJustin Hibbits 	if (if_gethwassist(ifp) & HN_CSUM_IP6_MASK)
24474db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_TXCSUM_IPV6, 0);
244815516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO4) {
24494db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_TSO4, 0);
24504db5958aSJustin Hibbits 		if_sethwassistbits(ifp, CSUM_IP_TSO, 0);
245115516c77SSepherosa Ziehau 	}
245215516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO6) {
24534db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_TSO6, 0);
24544db5958aSJustin Hibbits 		if_sethwassistbits(ifp, CSUM_IP6_TSO, 0);
245515516c77SSepherosa Ziehau 	}
245615516c77SSepherosa Ziehau 
245715516c77SSepherosa Ziehau 	/* Enable all available capabilities by default. */
24584db5958aSJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
245915516c77SSepherosa Ziehau 
24607960e6baSSepherosa Ziehau 	/*
24617960e6baSSepherosa Ziehau 	 * Disable IPv6 TSO and TXCSUM by default, they still can
24627960e6baSSepherosa Ziehau 	 * be enabled through SIOCSIFCAP.
24637960e6baSSepherosa Ziehau 	 */
24644db5958aSJustin Hibbits 	if_setcapenablebit(ifp, 0, (IFCAP_TXCSUM_IPV6 | IFCAP_TSO6));
24654db5958aSJustin Hibbits 	if_sethwassistbits(ifp, 0, (HN_CSUM_IP6_MASK | CSUM_IP6_TSO));
24667960e6baSSepherosa Ziehau 
24674db5958aSJustin Hibbits 	if (if_getcapabilities(ifp) & (IFCAP_TSO6 | IFCAP_TSO4)) {
24689c6cae24SSepherosa Ziehau 		/*
24699c6cae24SSepherosa Ziehau 		 * Lock hn_set_tso_maxsize() to simplify its
24709c6cae24SSepherosa Ziehau 		 * internal logic.
24719c6cae24SSepherosa Ziehau 		 */
24729c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
247315516c77SSepherosa Ziehau 		hn_set_tso_maxsize(sc, hn_tso_maxlen, ETHERMTU);
24749c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
24754db5958aSJustin Hibbits 		if_sethwtsomaxsegcount(ifp, HN_TX_DATA_SEGCNT_MAX);
24764db5958aSJustin Hibbits 		if_sethwtsomaxsegsize(ifp, PAGE_SIZE);
247715516c77SSepherosa Ziehau 	}
247815516c77SSepherosa Ziehau 
247915516c77SSepherosa Ziehau 	ether_ifattach(ifp, eaddr);
248015516c77SSepherosa Ziehau 
24814db5958aSJustin Hibbits 	if ((if_getcapabilities(ifp) & (IFCAP_TSO6 | IFCAP_TSO4)) && bootverbose) {
248215516c77SSepherosa Ziehau 		if_printf(ifp, "TSO segcnt %u segsz %u\n",
24834db5958aSJustin Hibbits 		    if_gethwtsomaxsegcount(ifp), if_gethwtsomaxsegsize(ifp));
248415516c77SSepherosa Ziehau 	}
2485eb2fe044SSepherosa Ziehau 	if (mtu < ETHERMTU) {
24864db5958aSJustin Hibbits 
24874db5958aSJustin Hibbits 		if_setmtu(ifp, mtu);
2488eb2fe044SSepherosa Ziehau 	}
248915516c77SSepherosa Ziehau 
249015516c77SSepherosa Ziehau 	/* Inform the upper layer about the long frame support. */
24914db5958aSJustin Hibbits 	if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
249215516c77SSepherosa Ziehau 
249315516c77SSepherosa Ziehau 	/*
249415516c77SSepherosa Ziehau 	 * Kick off link status check.
249515516c77SSepherosa Ziehau 	 */
249615516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
249715516c77SSepherosa Ziehau 	hn_update_link_status(sc);
249815516c77SSepherosa Ziehau 
24999c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
25005bdfd3fdSDexuan Cui 		sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event,
25015bdfd3fdSDexuan Cui 		    hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY);
25025bdfd3fdSDexuan Cui 		sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event,
25035bdfd3fdSDexuan Cui 		    hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY);
25049c6cae24SSepherosa Ziehau 	} else {
25059c6cae24SSepherosa Ziehau 		sc->hn_ifnet_lnkhand = EVENTHANDLER_REGISTER(ifnet_link_event,
25069c6cae24SSepherosa Ziehau 		    hn_ifnet_lnkevent, sc, EVENTHANDLER_PRI_ANY);
25079c6cae24SSepherosa Ziehau 	}
25085bdfd3fdSDexuan Cui 
2509f41e0df4SSepherosa Ziehau 	/*
2510f41e0df4SSepherosa Ziehau 	 * NOTE:
2511f41e0df4SSepherosa Ziehau 	 * Subscribe ether_ifattach event, instead of ifnet_arrival event,
2512f41e0df4SSepherosa Ziehau 	 * since interface's LLADDR is needed; interface LLADDR is not
2513f41e0df4SSepherosa Ziehau 	 * available when ifnet_arrival event is triggered.
2514f41e0df4SSepherosa Ziehau 	 */
2515499c3e17SSepherosa Ziehau 	sc->hn_ifnet_atthand = EVENTHANDLER_REGISTER(ether_ifattach_event,
2516499c3e17SSepherosa Ziehau 	    hn_ifnet_attevent, sc, EVENTHANDLER_PRI_ANY);
2517499c3e17SSepherosa Ziehau 	sc->hn_ifnet_dethand = EVENTHANDLER_REGISTER(ifnet_departure_event,
2518499c3e17SSepherosa Ziehau 	    hn_ifnet_detevent, sc, EVENTHANDLER_PRI_ANY);
2519499c3e17SSepherosa Ziehau 
252015516c77SSepherosa Ziehau 	return (0);
252115516c77SSepherosa Ziehau failed:
252215516c77SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
252315516c77SSepherosa Ziehau 		hn_synth_detach(sc);
252415516c77SSepherosa Ziehau 	hn_detach(dev);
252515516c77SSepherosa Ziehau 	return (error);
252615516c77SSepherosa Ziehau }
252715516c77SSepherosa Ziehau 
252815516c77SSepherosa Ziehau static int
252915516c77SSepherosa Ziehau hn_detach(device_t dev)
253015516c77SSepherosa Ziehau {
253115516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
25324db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp, vf_ifp;
253315516c77SSepherosa Ziehau 
25349c6cae24SSepherosa Ziehau 	if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
25359c6cae24SSepherosa Ziehau 		/*
25369c6cae24SSepherosa Ziehau 		 * In case that the vmbus missed the orphan handler
25379c6cae24SSepherosa Ziehau 		 * installation.
25389c6cae24SSepherosa Ziehau 		 */
25399c6cae24SSepherosa Ziehau 		vmbus_xact_ctx_orphan(sc->hn_xact);
25409c6cae24SSepherosa Ziehau 	}
25419c6cae24SSepherosa Ziehau 
25425bdfd3fdSDexuan Cui 	if (sc->hn_ifaddr_evthand != NULL)
25435bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand);
25445bdfd3fdSDexuan Cui 	if (sc->hn_ifnet_evthand != NULL)
25455bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand);
2546499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_atthand != NULL) {
2547499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ether_ifattach_event,
2548499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_atthand);
2549499c3e17SSepherosa Ziehau 	}
2550499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_dethand != NULL) {
2551499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
2552499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_dethand);
2553499c3e17SSepherosa Ziehau 	}
25549c6cae24SSepherosa Ziehau 	if (sc->hn_ifnet_lnkhand != NULL)
25559c6cae24SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_link_event, sc->hn_ifnet_lnkhand);
2556499c3e17SSepherosa Ziehau 
2557499c3e17SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
2558499c3e17SSepherosa Ziehau 	__compiler_membar();
2559499c3e17SSepherosa Ziehau 	if (vf_ifp != NULL)
2560499c3e17SSepherosa Ziehau 		hn_ifnet_detevent(sc, vf_ifp);
25615bdfd3fdSDexuan Cui 
256215516c77SSepherosa Ziehau 	if (device_is_attached(dev)) {
256315516c77SSepherosa Ziehau 		HN_LOCK(sc);
256415516c77SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
25654db5958aSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
25665bdfd3fdSDexuan Cui 				hn_stop(sc, true);
256715516c77SSepherosa Ziehau 			/*
256815516c77SSepherosa Ziehau 			 * NOTE:
256915516c77SSepherosa Ziehau 			 * hn_stop() only suspends data, so managment
257015516c77SSepherosa Ziehau 			 * stuffs have to be suspended manually here.
257115516c77SSepherosa Ziehau 			 */
257215516c77SSepherosa Ziehau 			hn_suspend_mgmt(sc);
257315516c77SSepherosa Ziehau 			hn_synth_detach(sc);
257415516c77SSepherosa Ziehau 		}
257515516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
257615516c77SSepherosa Ziehau 		ether_ifdetach(ifp);
257715516c77SSepherosa Ziehau 	}
257815516c77SSepherosa Ziehau 
257915516c77SSepherosa Ziehau 	ifmedia_removeall(&sc->hn_media);
258015516c77SSepherosa Ziehau 	hn_destroy_rx_data(sc);
258115516c77SSepherosa Ziehau 	hn_destroy_tx_data(sc);
258215516c77SSepherosa Ziehau 
25830e11868dSSepherosa Ziehau 	if (sc->hn_tx_taskqs != NULL && sc->hn_tx_taskqs != hn_tx_taskque) {
2584fdd0222aSSepherosa Ziehau 		int i;
2585fdd0222aSSepherosa Ziehau 
2586fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
2587fdd0222aSSepherosa Ziehau 			taskqueue_free(sc->hn_tx_taskqs[i]);
2588fdd0222aSSepherosa Ziehau 		free(sc->hn_tx_taskqs, M_DEVBUF);
2589fdd0222aSSepherosa Ziehau 	}
259015516c77SSepherosa Ziehau 	taskqueue_free(sc->hn_mgmt_taskq0);
25919c6cae24SSepherosa Ziehau 	if (sc->hn_vf_taskq != NULL)
25929c6cae24SSepherosa Ziehau 		taskqueue_free(sc->hn_vf_taskq);
259315516c77SSepherosa Ziehau 
259425641fc7SSepherosa Ziehau 	if (sc->hn_xact != NULL) {
259525641fc7SSepherosa Ziehau 		/*
259625641fc7SSepherosa Ziehau 		 * Uninstall the orphan handler _before_ the xact is
259725641fc7SSepherosa Ziehau 		 * destructed.
259825641fc7SSepherosa Ziehau 		 */
259925641fc7SSepherosa Ziehau 		vmbus_chan_unset_orphan(sc->hn_prichan);
260015516c77SSepherosa Ziehau 		vmbus_xact_ctx_destroy(sc->hn_xact);
260125641fc7SSepherosa Ziehau 	}
260215516c77SSepherosa Ziehau 
260315516c77SSepherosa Ziehau 	if_free(ifp);
260415516c77SSepherosa Ziehau 
260515516c77SSepherosa Ziehau 	HN_LOCK_DESTROY(sc);
26069c6cae24SSepherosa Ziehau 	rm_destroy(&sc->hn_vf_lock);
260715516c77SSepherosa Ziehau 	return (0);
260815516c77SSepherosa Ziehau }
260915516c77SSepherosa Ziehau 
261015516c77SSepherosa Ziehau static int
261115516c77SSepherosa Ziehau hn_shutdown(device_t dev)
261215516c77SSepherosa Ziehau {
261315516c77SSepherosa Ziehau 
261415516c77SSepherosa Ziehau 	return (0);
261515516c77SSepherosa Ziehau }
261615516c77SSepherosa Ziehau 
261715516c77SSepherosa Ziehau static void
261815516c77SSepherosa Ziehau hn_link_status(struct hn_softc *sc)
261915516c77SSepherosa Ziehau {
262015516c77SSepherosa Ziehau 	uint32_t link_status;
262115516c77SSepherosa Ziehau 	int error;
262215516c77SSepherosa Ziehau 
262315516c77SSepherosa Ziehau 	error = hn_rndis_get_linkstatus(sc, &link_status);
262415516c77SSepherosa Ziehau 	if (error) {
262515516c77SSepherosa Ziehau 		/* XXX what to do? */
262615516c77SSepherosa Ziehau 		return;
262715516c77SSepherosa Ziehau 	}
262815516c77SSepherosa Ziehau 
262915516c77SSepherosa Ziehau 	if (link_status == NDIS_MEDIA_STATE_CONNECTED)
263015516c77SSepherosa Ziehau 		sc->hn_link_flags |= HN_LINK_FLAG_LINKUP;
263115516c77SSepherosa Ziehau 	else
263215516c77SSepherosa Ziehau 		sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
263315516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp,
263415516c77SSepherosa Ziehau 	    (sc->hn_link_flags & HN_LINK_FLAG_LINKUP) ?
263515516c77SSepherosa Ziehau 	    LINK_STATE_UP : LINK_STATE_DOWN);
263615516c77SSepherosa Ziehau }
263715516c77SSepherosa Ziehau 
263815516c77SSepherosa Ziehau static void
263915516c77SSepherosa Ziehau hn_link_taskfunc(void *xsc, int pending __unused)
264015516c77SSepherosa Ziehau {
264115516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
264215516c77SSepherosa Ziehau 
264315516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
264415516c77SSepherosa Ziehau 		return;
264515516c77SSepherosa Ziehau 	hn_link_status(sc);
264615516c77SSepherosa Ziehau }
264715516c77SSepherosa Ziehau 
264815516c77SSepherosa Ziehau static void
264915516c77SSepherosa Ziehau hn_netchg_init_taskfunc(void *xsc, int pending __unused)
265015516c77SSepherosa Ziehau {
265115516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
265215516c77SSepherosa Ziehau 
265315516c77SSepherosa Ziehau 	/* Prevent any link status checks from running. */
265415516c77SSepherosa Ziehau 	sc->hn_link_flags |= HN_LINK_FLAG_NETCHG;
265515516c77SSepherosa Ziehau 
265615516c77SSepherosa Ziehau 	/*
265715516c77SSepherosa Ziehau 	 * Fake up a [link down --> link up] state change; 5 seconds
265815516c77SSepherosa Ziehau 	 * delay is used, which closely simulates miibus reaction
265915516c77SSepherosa Ziehau 	 * upon link down event.
266015516c77SSepherosa Ziehau 	 */
266115516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
266215516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
266315516c77SSepherosa Ziehau 	taskqueue_enqueue_timeout(sc->hn_mgmt_taskq0,
266415516c77SSepherosa Ziehau 	    &sc->hn_netchg_status, 5 * hz);
266515516c77SSepherosa Ziehau }
266615516c77SSepherosa Ziehau 
266715516c77SSepherosa Ziehau static void
266815516c77SSepherosa Ziehau hn_netchg_status_taskfunc(void *xsc, int pending __unused)
266915516c77SSepherosa Ziehau {
267015516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
267115516c77SSepherosa Ziehau 
267215516c77SSepherosa Ziehau 	/* Re-allow link status checks. */
267315516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_NETCHG;
267415516c77SSepherosa Ziehau 	hn_link_status(sc);
267515516c77SSepherosa Ziehau }
267615516c77SSepherosa Ziehau 
267715516c77SSepherosa Ziehau static void
267815516c77SSepherosa Ziehau hn_update_link_status(struct hn_softc *sc)
267915516c77SSepherosa Ziehau {
268015516c77SSepherosa Ziehau 
268115516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
268215516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
268315516c77SSepherosa Ziehau }
268415516c77SSepherosa Ziehau 
268515516c77SSepherosa Ziehau static void
268615516c77SSepherosa Ziehau hn_change_network(struct hn_softc *sc)
268715516c77SSepherosa Ziehau {
268815516c77SSepherosa Ziehau 
268915516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
269015516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_netchg_init);
269115516c77SSepherosa Ziehau }
269215516c77SSepherosa Ziehau 
269315516c77SSepherosa Ziehau static __inline int
269415516c77SSepherosa Ziehau hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
269515516c77SSepherosa Ziehau     struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
269615516c77SSepherosa Ziehau {
269715516c77SSepherosa Ziehau 	struct mbuf *m = *m_head;
269815516c77SSepherosa Ziehau 	int error;
269915516c77SSepherosa Ziehau 
270015516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID, ("txd uses chim"));
270115516c77SSepherosa Ziehau 
270215516c77SSepherosa Ziehau 	error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag, txd->data_dmap,
270315516c77SSepherosa Ziehau 	    m, segs, nsegs, BUS_DMA_NOWAIT);
270415516c77SSepherosa Ziehau 	if (error == EFBIG) {
270515516c77SSepherosa Ziehau 		struct mbuf *m_new;
270615516c77SSepherosa Ziehau 
270715516c77SSepherosa Ziehau 		m_new = m_collapse(m, M_NOWAIT, HN_TX_DATA_SEGCNT_MAX);
270815516c77SSepherosa Ziehau 		if (m_new == NULL)
270915516c77SSepherosa Ziehau 			return ENOBUFS;
271015516c77SSepherosa Ziehau 		else
271115516c77SSepherosa Ziehau 			*m_head = m = m_new;
271215516c77SSepherosa Ziehau 		txr->hn_tx_collapsed++;
271315516c77SSepherosa Ziehau 
271415516c77SSepherosa Ziehau 		error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag,
271515516c77SSepherosa Ziehau 		    txd->data_dmap, m, segs, nsegs, BUS_DMA_NOWAIT);
271615516c77SSepherosa Ziehau 	}
271715516c77SSepherosa Ziehau 	if (!error) {
271815516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag, txd->data_dmap,
271915516c77SSepherosa Ziehau 		    BUS_DMASYNC_PREWRITE);
272015516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_DMAMAP;
272115516c77SSepherosa Ziehau 	}
272215516c77SSepherosa Ziehau 	return error;
272315516c77SSepherosa Ziehau }
272415516c77SSepherosa Ziehau 
272515516c77SSepherosa Ziehau static __inline int
272615516c77SSepherosa Ziehau hn_txdesc_put(struct hn_tx_ring *txr, struct hn_txdesc *txd)
272715516c77SSepherosa Ziehau {
272815516c77SSepherosa Ziehau 
272915516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONLIST) == 0,
273015516c77SSepherosa Ziehau 	    ("put an onlist txd %#x", txd->flags));
2731dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2732dc13fee6SSepherosa Ziehau 	    ("put an onagg txd %#x", txd->flags));
273315516c77SSepherosa Ziehau 
273415516c77SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
273515516c77SSepherosa Ziehau 	if (atomic_fetchadd_int(&txd->refs, -1) != 1)
273615516c77SSepherosa Ziehau 		return 0;
273715516c77SSepherosa Ziehau 
2738dc13fee6SSepherosa Ziehau 	if (!STAILQ_EMPTY(&txd->agg_list)) {
2739dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tmp_txd;
2740dc13fee6SSepherosa Ziehau 
2741dc13fee6SSepherosa Ziehau 		while ((tmp_txd = STAILQ_FIRST(&txd->agg_list)) != NULL) {
2742a0f49d67SMateusz Guzik 			int freed __diagused;
2743dc13fee6SSepherosa Ziehau 
2744dc13fee6SSepherosa Ziehau 			KASSERT(STAILQ_EMPTY(&tmp_txd->agg_list),
2745dc13fee6SSepherosa Ziehau 			    ("resursive aggregation on aggregated txdesc"));
2746dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_ONAGG),
2747dc13fee6SSepherosa Ziehau 			    ("not aggregated txdesc"));
2748dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
2749dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc uses dmamap"));
2750dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
2751dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc consumes "
2752dc13fee6SSepherosa Ziehau 			     "chimney sending buffer"));
2753dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_size == 0,
2754dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc has non-zero "
2755dc13fee6SSepherosa Ziehau 			     "chimney sending size"));
2756dc13fee6SSepherosa Ziehau 
2757dc13fee6SSepherosa Ziehau 			STAILQ_REMOVE_HEAD(&txd->agg_list, agg_link);
2758dc13fee6SSepherosa Ziehau 			tmp_txd->flags &= ~HN_TXD_FLAG_ONAGG;
2759dc13fee6SSepherosa Ziehau 			freed = hn_txdesc_put(txr, tmp_txd);
2760dc13fee6SSepherosa Ziehau 			KASSERT(freed, ("failed to free aggregated txdesc"));
2761dc13fee6SSepherosa Ziehau 		}
2762dc13fee6SSepherosa Ziehau 	}
2763dc13fee6SSepherosa Ziehau 
276415516c77SSepherosa Ziehau 	if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) {
276515516c77SSepherosa Ziehau 		KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
276615516c77SSepherosa Ziehau 		    ("chim txd uses dmamap"));
276715516c77SSepherosa Ziehau 		hn_chim_free(txr->hn_sc, txd->chim_index);
276815516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
2769dc13fee6SSepherosa Ziehau 		txd->chim_size = 0;
277015516c77SSepherosa Ziehau 	} else if (txd->flags & HN_TXD_FLAG_DMAMAP) {
277115516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag,
277215516c77SSepherosa Ziehau 		    txd->data_dmap, BUS_DMASYNC_POSTWRITE);
277315516c77SSepherosa Ziehau 		bus_dmamap_unload(txr->hn_tx_data_dtag,
277415516c77SSepherosa Ziehau 		    txd->data_dmap);
277515516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_DMAMAP;
277615516c77SSepherosa Ziehau 	}
277715516c77SSepherosa Ziehau 
277815516c77SSepherosa Ziehau 	if (txd->m != NULL) {
277915516c77SSepherosa Ziehau 		m_freem(txd->m);
278015516c77SSepherosa Ziehau 		txd->m = NULL;
278115516c77SSepherosa Ziehau 	}
278215516c77SSepherosa Ziehau 
278315516c77SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONLIST;
278415516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
278515516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
278615516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_avail >= 0 &&
278715516c77SSepherosa Ziehau 	    txr->hn_txdesc_avail < txr->hn_txdesc_cnt,
278815516c77SSepherosa Ziehau 	    ("txdesc_put: invalid txd avail %d", txr->hn_txdesc_avail));
278915516c77SSepherosa Ziehau 	txr->hn_txdesc_avail++;
279015516c77SSepherosa Ziehau 	SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
279115516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
279285e4ae1eSSepherosa Ziehau #else	/* HN_USE_TXDESC_BUFRING */
279385e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
279415516c77SSepherosa Ziehau 	atomic_add_int(&txr->hn_txdesc_avail, 1);
279515516c77SSepherosa Ziehau #endif
279685e4ae1eSSepherosa Ziehau 	buf_ring_enqueue(txr->hn_txdesc_br, txd);
279785e4ae1eSSepherosa Ziehau #endif	/* !HN_USE_TXDESC_BUFRING */
279815516c77SSepherosa Ziehau 
279915516c77SSepherosa Ziehau 	return 1;
280015516c77SSepherosa Ziehau }
280115516c77SSepherosa Ziehau 
280215516c77SSepherosa Ziehau static __inline struct hn_txdesc *
280315516c77SSepherosa Ziehau hn_txdesc_get(struct hn_tx_ring *txr)
280415516c77SSepherosa Ziehau {
280515516c77SSepherosa Ziehau 	struct hn_txdesc *txd;
280615516c77SSepherosa Ziehau 
280715516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
280815516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
280915516c77SSepherosa Ziehau 	txd = SLIST_FIRST(&txr->hn_txlist);
281015516c77SSepherosa Ziehau 	if (txd != NULL) {
281115516c77SSepherosa Ziehau 		KASSERT(txr->hn_txdesc_avail > 0,
281215516c77SSepherosa Ziehau 		    ("txdesc_get: invalid txd avail %d", txr->hn_txdesc_avail));
281315516c77SSepherosa Ziehau 		txr->hn_txdesc_avail--;
281415516c77SSepherosa Ziehau 		SLIST_REMOVE_HEAD(&txr->hn_txlist, link);
281515516c77SSepherosa Ziehau 	}
281615516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
281715516c77SSepherosa Ziehau #else
281815516c77SSepherosa Ziehau 	txd = buf_ring_dequeue_sc(txr->hn_txdesc_br);
281915516c77SSepherosa Ziehau #endif
282015516c77SSepherosa Ziehau 
282115516c77SSepherosa Ziehau 	if (txd != NULL) {
282215516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
282385e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
282415516c77SSepherosa Ziehau 		atomic_subtract_int(&txr->hn_txdesc_avail, 1);
282515516c77SSepherosa Ziehau #endif
282685e4ae1eSSepherosa Ziehau #endif	/* HN_USE_TXDESC_BUFRING */
282715516c77SSepherosa Ziehau 		KASSERT(txd->m == NULL && txd->refs == 0 &&
2828dc13fee6SSepherosa Ziehau 		    STAILQ_EMPTY(&txd->agg_list) &&
282915516c77SSepherosa Ziehau 		    txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
2830dc13fee6SSepherosa Ziehau 		    txd->chim_size == 0 &&
283115516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONLIST) &&
2832dc13fee6SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONAGG) == 0 &&
283315516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("invalid txd"));
283415516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_ONLIST;
283515516c77SSepherosa Ziehau 		txd->refs = 1;
283615516c77SSepherosa Ziehau 	}
283715516c77SSepherosa Ziehau 	return txd;
283815516c77SSepherosa Ziehau }
283915516c77SSepherosa Ziehau 
284015516c77SSepherosa Ziehau static __inline void
284115516c77SSepherosa Ziehau hn_txdesc_hold(struct hn_txdesc *txd)
284215516c77SSepherosa Ziehau {
284315516c77SSepherosa Ziehau 
284415516c77SSepherosa Ziehau 	/* 0->1 transition will never work */
284525641fc7SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
284615516c77SSepherosa Ziehau 	atomic_add_int(&txd->refs, 1);
284715516c77SSepherosa Ziehau }
284815516c77SSepherosa Ziehau 
2849dc13fee6SSepherosa Ziehau static __inline void
2850dc13fee6SSepherosa Ziehau hn_txdesc_agg(struct hn_txdesc *agg_txd, struct hn_txdesc *txd)
2851dc13fee6SSepherosa Ziehau {
2852dc13fee6SSepherosa Ziehau 
2853dc13fee6SSepherosa Ziehau 	KASSERT((agg_txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2854dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on aggregating txdesc"));
2855dc13fee6SSepherosa Ziehau 
2856dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2857dc13fee6SSepherosa Ziehau 	    ("already aggregated"));
2858dc13fee6SSepherosa Ziehau 	KASSERT(STAILQ_EMPTY(&txd->agg_list),
2859dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on to-be-aggregated txdesc"));
2860dc13fee6SSepherosa Ziehau 
2861dc13fee6SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONAGG;
2862dc13fee6SSepherosa Ziehau 	STAILQ_INSERT_TAIL(&agg_txd->agg_list, txd, agg_link);
2863dc13fee6SSepherosa Ziehau }
2864dc13fee6SSepherosa Ziehau 
286515516c77SSepherosa Ziehau static bool
286615516c77SSepherosa Ziehau hn_tx_ring_pending(struct hn_tx_ring *txr)
286715516c77SSepherosa Ziehau {
286815516c77SSepherosa Ziehau 	bool pending = false;
286915516c77SSepherosa Ziehau 
287015516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
287115516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
287215516c77SSepherosa Ziehau 	if (txr->hn_txdesc_avail != txr->hn_txdesc_cnt)
287315516c77SSepherosa Ziehau 		pending = true;
287415516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
287515516c77SSepherosa Ziehau #else
287615516c77SSepherosa Ziehau 	if (!buf_ring_full(txr->hn_txdesc_br))
287715516c77SSepherosa Ziehau 		pending = true;
287815516c77SSepherosa Ziehau #endif
287915516c77SSepherosa Ziehau 	return (pending);
288015516c77SSepherosa Ziehau }
288115516c77SSepherosa Ziehau 
288215516c77SSepherosa Ziehau static __inline void
288315516c77SSepherosa Ziehau hn_txeof(struct hn_tx_ring *txr)
288415516c77SSepherosa Ziehau {
288515516c77SSepherosa Ziehau 	txr->hn_has_txeof = 0;
288615516c77SSepherosa Ziehau 	txr->hn_txeof(txr);
288715516c77SSepherosa Ziehau }
288815516c77SSepherosa Ziehau 
288915516c77SSepherosa Ziehau static void
289015516c77SSepherosa Ziehau hn_txpkt_done(struct hn_nvs_sendctx *sndc, struct hn_softc *sc,
289115516c77SSepherosa Ziehau     struct vmbus_channel *chan, const void *data __unused, int dlen __unused)
289215516c77SSepherosa Ziehau {
289315516c77SSepherosa Ziehau 	struct hn_txdesc *txd = sndc->hn_cbarg;
289415516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
289515516c77SSepherosa Ziehau 
289615516c77SSepherosa Ziehau 	txr = txd->txr;
289715516c77SSepherosa Ziehau 	KASSERT(txr->hn_chan == chan,
289815516c77SSepherosa Ziehau 	    ("channel mismatch, on chan%u, should be chan%u",
2899aa1a2adcSSepherosa Ziehau 	     vmbus_chan_id(chan), vmbus_chan_id(txr->hn_chan)));
290015516c77SSepherosa Ziehau 
290115516c77SSepherosa Ziehau 	txr->hn_has_txeof = 1;
290215516c77SSepherosa Ziehau 	hn_txdesc_put(txr, txd);
290315516c77SSepherosa Ziehau 
290415516c77SSepherosa Ziehau 	++txr->hn_txdone_cnt;
290515516c77SSepherosa Ziehau 	if (txr->hn_txdone_cnt >= HN_EARLY_TXEOF_THRESH) {
290615516c77SSepherosa Ziehau 		txr->hn_txdone_cnt = 0;
290715516c77SSepherosa Ziehau 		if (txr->hn_oactive)
290815516c77SSepherosa Ziehau 			hn_txeof(txr);
290915516c77SSepherosa Ziehau 	}
291015516c77SSepherosa Ziehau }
291115516c77SSepherosa Ziehau 
291215516c77SSepherosa Ziehau static void
291315516c77SSepherosa Ziehau hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
291415516c77SSepherosa Ziehau {
291515516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
291626d79d40SMichael Tuexen 	struct epoch_tracker et;
291726d79d40SMichael Tuexen 
291826d79d40SMichael Tuexen 	NET_EPOCH_ENTER(et);
291915516c77SSepherosa Ziehau 	tcp_lro_flush_all(&rxr->hn_lro);
292026d79d40SMichael Tuexen 	NET_EPOCH_EXIT(et);
292115516c77SSepherosa Ziehau #endif
292215516c77SSepherosa Ziehau 
292315516c77SSepherosa Ziehau 	/*
292415516c77SSepherosa Ziehau 	 * NOTE:
292515516c77SSepherosa Ziehau 	 * 'txr' could be NULL, if multiple channels and
292615516c77SSepherosa Ziehau 	 * ifnet.if_start method are enabled.
292715516c77SSepherosa Ziehau 	 */
292815516c77SSepherosa Ziehau 	if (txr == NULL || !txr->hn_has_txeof)
292915516c77SSepherosa Ziehau 		return;
293015516c77SSepherosa Ziehau 
293115516c77SSepherosa Ziehau 	txr->hn_txdone_cnt = 0;
293215516c77SSepherosa Ziehau 	hn_txeof(txr);
293315516c77SSepherosa Ziehau }
293415516c77SSepherosa Ziehau 
293515516c77SSepherosa Ziehau static __inline uint32_t
293615516c77SSepherosa Ziehau hn_rndis_pktmsg_offset(uint32_t ofs)
293715516c77SSepherosa Ziehau {
293815516c77SSepherosa Ziehau 
293915516c77SSepherosa Ziehau 	KASSERT(ofs >= sizeof(struct rndis_packet_msg),
294015516c77SSepherosa Ziehau 	    ("invalid RNDIS packet msg offset %u", ofs));
294115516c77SSepherosa Ziehau 	return (ofs - __offsetof(struct rndis_packet_msg, rm_dataoffset));
294215516c77SSepherosa Ziehau }
294315516c77SSepherosa Ziehau 
294415516c77SSepherosa Ziehau static __inline void *
294515516c77SSepherosa Ziehau hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
294615516c77SSepherosa Ziehau     size_t pi_dlen, uint32_t pi_type)
294715516c77SSepherosa Ziehau {
294815516c77SSepherosa Ziehau 	const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
294915516c77SSepherosa Ziehau 	struct rndis_pktinfo *pi;
295015516c77SSepherosa Ziehau 
295115516c77SSepherosa Ziehau 	KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
295215516c77SSepherosa Ziehau 	    ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
295315516c77SSepherosa Ziehau 
295415516c77SSepherosa Ziehau 	/*
295515516c77SSepherosa Ziehau 	 * Per-packet-info does not move; it only grows.
295615516c77SSepherosa Ziehau 	 *
295715516c77SSepherosa Ziehau 	 * NOTE:
295815516c77SSepherosa Ziehau 	 * rm_pktinfooffset in this phase counts from the beginning
295915516c77SSepherosa Ziehau 	 * of rndis_packet_msg.
296015516c77SSepherosa Ziehau 	 */
296115516c77SSepherosa Ziehau 	KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
296215516c77SSepherosa Ziehau 	    ("%u pktinfo overflows RNDIS packet msg", pi_type));
296315516c77SSepherosa Ziehau 	pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
296415516c77SSepherosa Ziehau 	    pkt->rm_pktinfolen);
296515516c77SSepherosa Ziehau 	pkt->rm_pktinfolen += pi_size;
296615516c77SSepherosa Ziehau 
296715516c77SSepherosa Ziehau 	pi->rm_size = pi_size;
296815516c77SSepherosa Ziehau 	pi->rm_type = pi_type;
2969805dbff6SWei Hu 	pi->rm_internal = 0;
297015516c77SSepherosa Ziehau 	pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
297115516c77SSepherosa Ziehau 
297215516c77SSepherosa Ziehau 	return (pi->rm_data);
297315516c77SSepherosa Ziehau }
297415516c77SSepherosa Ziehau 
2975dc13fee6SSepherosa Ziehau static __inline int
29764db5958aSJustin Hibbits hn_flush_txagg(if_t ifp, struct hn_tx_ring *txr)
2977dc13fee6SSepherosa Ziehau {
2978dc13fee6SSepherosa Ziehau 	struct hn_txdesc *txd;
2979dc13fee6SSepherosa Ziehau 	struct mbuf *m;
2980dc13fee6SSepherosa Ziehau 	int error, pkts;
2981dc13fee6SSepherosa Ziehau 
2982dc13fee6SSepherosa Ziehau 	txd = txr->hn_agg_txd;
2983dc13fee6SSepherosa Ziehau 	KASSERT(txd != NULL, ("no aggregate txdesc"));
2984dc13fee6SSepherosa Ziehau 
2985dc13fee6SSepherosa Ziehau 	/*
2986dc13fee6SSepherosa Ziehau 	 * Since hn_txpkt() will reset this temporary stat, save
2987dc13fee6SSepherosa Ziehau 	 * it now, so that oerrors can be updated properly, if
2988dc13fee6SSepherosa Ziehau 	 * hn_txpkt() ever fails.
2989dc13fee6SSepherosa Ziehau 	 */
2990dc13fee6SSepherosa Ziehau 	pkts = txr->hn_stat_pkts;
2991dc13fee6SSepherosa Ziehau 
2992dc13fee6SSepherosa Ziehau 	/*
2993dc13fee6SSepherosa Ziehau 	 * Since txd's mbuf will _not_ be freed upon hn_txpkt()
2994dc13fee6SSepherosa Ziehau 	 * failure, save it for later freeing, if hn_txpkt() ever
2995dc13fee6SSepherosa Ziehau 	 * fails.
2996dc13fee6SSepherosa Ziehau 	 */
2997dc13fee6SSepherosa Ziehau 	m = txd->m;
2998dc13fee6SSepherosa Ziehau 	error = hn_txpkt(ifp, txr, txd);
2999dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
3000dc13fee6SSepherosa Ziehau 		/* txd is freed, but m is not. */
3001dc13fee6SSepherosa Ziehau 		m_freem(m);
3002dc13fee6SSepherosa Ziehau 
3003dc13fee6SSepherosa Ziehau 		txr->hn_flush_failed++;
3004dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, pkts);
3005dc13fee6SSepherosa Ziehau 	}
3006dc13fee6SSepherosa Ziehau 
3007dc13fee6SSepherosa Ziehau 	/* Reset all aggregation states. */
3008dc13fee6SSepherosa Ziehau 	txr->hn_agg_txd = NULL;
3009dc13fee6SSepherosa Ziehau 	txr->hn_agg_szleft = 0;
3010dc13fee6SSepherosa Ziehau 	txr->hn_agg_pktleft = 0;
3011dc13fee6SSepherosa Ziehau 	txr->hn_agg_prevpkt = NULL;
3012dc13fee6SSepherosa Ziehau 
3013dc13fee6SSepherosa Ziehau 	return (error);
3014dc13fee6SSepherosa Ziehau }
3015dc13fee6SSepherosa Ziehau 
3016dc13fee6SSepherosa Ziehau static void *
30174db5958aSJustin Hibbits hn_try_txagg(if_t ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
3018dc13fee6SSepherosa Ziehau     int pktsize)
3019dc13fee6SSepherosa Ziehau {
3020dc13fee6SSepherosa Ziehau 	void *chim;
3021dc13fee6SSepherosa Ziehau 
3022dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL) {
3023dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft >= 1 && txr->hn_agg_szleft > pktsize) {
3024dc13fee6SSepherosa Ziehau 			struct hn_txdesc *agg_txd = txr->hn_agg_txd;
3025dc13fee6SSepherosa Ziehau 			struct rndis_packet_msg *pkt = txr->hn_agg_prevpkt;
3026dc13fee6SSepherosa Ziehau 			int olen;
3027dc13fee6SSepherosa Ziehau 
3028dc13fee6SSepherosa Ziehau 			/*
3029dc13fee6SSepherosa Ziehau 			 * Update the previous RNDIS packet's total length,
3030dc13fee6SSepherosa Ziehau 			 * it can be increased due to the mandatory alignment
3031dc13fee6SSepherosa Ziehau 			 * padding for this RNDIS packet.  And update the
3032dc13fee6SSepherosa Ziehau 			 * aggregating txdesc's chimney sending buffer size
3033dc13fee6SSepherosa Ziehau 			 * accordingly.
3034dc13fee6SSepherosa Ziehau 			 *
3035dc13fee6SSepherosa Ziehau 			 * XXX
3036dc13fee6SSepherosa Ziehau 			 * Zero-out the padding, as required by the RNDIS spec.
3037dc13fee6SSepherosa Ziehau 			 */
3038dc13fee6SSepherosa Ziehau 			olen = pkt->rm_len;
3039dc13fee6SSepherosa Ziehau 			pkt->rm_len = roundup2(olen, txr->hn_agg_align);
3040dc13fee6SSepherosa Ziehau 			agg_txd->chim_size += pkt->rm_len - olen;
3041dc13fee6SSepherosa Ziehau 
3042dc13fee6SSepherosa Ziehau 			/* Link this txdesc to the parent. */
3043dc13fee6SSepherosa Ziehau 			hn_txdesc_agg(agg_txd, txd);
3044dc13fee6SSepherosa Ziehau 
3045dc13fee6SSepherosa Ziehau 			chim = (uint8_t *)pkt + pkt->rm_len;
3046dc13fee6SSepherosa Ziehau 			/* Save the current packet for later fixup. */
3047dc13fee6SSepherosa Ziehau 			txr->hn_agg_prevpkt = chim;
3048dc13fee6SSepherosa Ziehau 
3049dc13fee6SSepherosa Ziehau 			txr->hn_agg_pktleft--;
3050dc13fee6SSepherosa Ziehau 			txr->hn_agg_szleft -= pktsize;
3051dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_szleft <=
3052dc13fee6SSepherosa Ziehau 			    HN_PKTSIZE_MIN(txr->hn_agg_align)) {
3053dc13fee6SSepherosa Ziehau 				/*
3054dc13fee6SSepherosa Ziehau 				 * Probably can't aggregate more packets,
3055dc13fee6SSepherosa Ziehau 				 * flush this aggregating txdesc proactively.
3056dc13fee6SSepherosa Ziehau 				 */
3057dc13fee6SSepherosa Ziehau 				txr->hn_agg_pktleft = 0;
3058dc13fee6SSepherosa Ziehau 			}
3059dc13fee6SSepherosa Ziehau 			/* Done! */
3060dc13fee6SSepherosa Ziehau 			return (chim);
3061dc13fee6SSepherosa Ziehau 		}
3062dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
3063dc13fee6SSepherosa Ziehau 	}
3064dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
3065dc13fee6SSepherosa Ziehau 
3066dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney_tried++;
3067dc13fee6SSepherosa Ziehau 	txd->chim_index = hn_chim_alloc(txr->hn_sc);
3068dc13fee6SSepherosa Ziehau 	if (txd->chim_index == HN_NVS_CHIM_IDX_INVALID)
3069dc13fee6SSepherosa Ziehau 		return (NULL);
3070dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney++;
3071dc13fee6SSepherosa Ziehau 
3072dc13fee6SSepherosa Ziehau 	chim = txr->hn_sc->hn_chim +
3073dc13fee6SSepherosa Ziehau 	    (txd->chim_index * txr->hn_sc->hn_chim_szmax);
3074dc13fee6SSepherosa Ziehau 
3075dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_pktmax > 1 &&
3076dc13fee6SSepherosa Ziehau 	    txr->hn_agg_szmax > pktsize + HN_PKTSIZE_MIN(txr->hn_agg_align)) {
3077dc13fee6SSepherosa Ziehau 		txr->hn_agg_txd = txd;
3078dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktleft = txr->hn_agg_pktmax - 1;
3079dc13fee6SSepherosa Ziehau 		txr->hn_agg_szleft = txr->hn_agg_szmax - pktsize;
3080dc13fee6SSepherosa Ziehau 		txr->hn_agg_prevpkt = chim;
3081dc13fee6SSepherosa Ziehau 	}
3082dc13fee6SSepherosa Ziehau 	return (chim);
3083dc13fee6SSepherosa Ziehau }
3084dc13fee6SSepherosa Ziehau 
308515516c77SSepherosa Ziehau /*
308615516c77SSepherosa Ziehau  * NOTE:
308715516c77SSepherosa Ziehau  * If this function fails, then both txd and m_head0 will be freed.
308815516c77SSepherosa Ziehau  */
308915516c77SSepherosa Ziehau static int
30904db5958aSJustin Hibbits hn_encap(if_t ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
3091dc13fee6SSepherosa Ziehau     struct mbuf **m_head0)
309215516c77SSepherosa Ziehau {
309315516c77SSepherosa Ziehau 	bus_dma_segment_t segs[HN_TX_DATA_SEGCNT_MAX];
309415516c77SSepherosa Ziehau 	int error, nsegs, i;
309515516c77SSepherosa Ziehau 	struct mbuf *m_head = *m_head0;
309615516c77SSepherosa Ziehau 	struct rndis_packet_msg *pkt;
309715516c77SSepherosa Ziehau 	uint32_t *pi_data;
30988966e5d5SSepherosa Ziehau 	void *chim = NULL;
3099dc13fee6SSepherosa Ziehau 	int pkt_hlen, pkt_size;
310015516c77SSepherosa Ziehau 
310115516c77SSepherosa Ziehau 	pkt = txd->rndis_pkt;
3102dc13fee6SSepherosa Ziehau 	pkt_size = HN_PKTSIZE(m_head, txr->hn_agg_align);
3103dc13fee6SSepherosa Ziehau 	if (pkt_size < txr->hn_chim_size) {
3104dc13fee6SSepherosa Ziehau 		chim = hn_try_txagg(ifp, txr, txd, pkt_size);
3105dc13fee6SSepherosa Ziehau 		if (chim != NULL)
31068966e5d5SSepherosa Ziehau 			pkt = chim;
3107dc13fee6SSepherosa Ziehau 	} else {
3108dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL)
3109dc13fee6SSepherosa Ziehau 			hn_flush_txagg(ifp, txr);
31108966e5d5SSepherosa Ziehau 	}
31118966e5d5SSepherosa Ziehau 
311215516c77SSepherosa Ziehau 	pkt->rm_type = REMOTE_NDIS_PACKET_MSG;
31138fe90f73SSepherosa Ziehau 	pkt->rm_len = m_head->m_pkthdr.len;
31149130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = 0;
311515516c77SSepherosa Ziehau 	pkt->rm_datalen = m_head->m_pkthdr.len;
3116dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataoffset = 0;
3117dc13fee6SSepherosa Ziehau 	pkt->rm_oobdatalen = 0;
3118dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataelements = 0;
311915516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = sizeof(*pkt);
312015516c77SSepherosa Ziehau 	pkt->rm_pktinfolen = 0;
3121dc13fee6SSepherosa Ziehau 	pkt->rm_vchandle = 0;
3122dc13fee6SSepherosa Ziehau 	pkt->rm_reserved = 0;
312315516c77SSepherosa Ziehau 
312415516c77SSepherosa Ziehau 	if (txr->hn_tx_flags & HN_TX_FLAG_HASHVAL) {
312515516c77SSepherosa Ziehau 		/*
312680f39bd9SWei Hu 		 * Set the hash value for this packet.
312715516c77SSepherosa Ziehau 		 */
312815516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
312915516c77SSepherosa Ziehau 		    HN_NDIS_HASH_VALUE_SIZE, HN_NDIS_PKTINFO_TYPE_HASHVAL);
313080f39bd9SWei Hu 
313180f39bd9SWei Hu 		if (M_HASHTYPE_ISHASH(m_head))
313280f39bd9SWei Hu 			/*
313380f39bd9SWei Hu 			 * The flowid field contains the hash value host
313480f39bd9SWei Hu 			 * set in the rx queue if it is a ip forwarding pkt.
313580f39bd9SWei Hu 			 * Set the same hash value so host can send on the
313680f39bd9SWei Hu 			 * cpu it was received.
313780f39bd9SWei Hu 			 */
313880f39bd9SWei Hu 			*pi_data = m_head->m_pkthdr.flowid;
313980f39bd9SWei Hu 		else
314080f39bd9SWei Hu 			/*
314180f39bd9SWei Hu 			 * Otherwise just put the tx queue index.
314280f39bd9SWei Hu 			 */
314315516c77SSepherosa Ziehau 			*pi_data = txr->hn_tx_idx;
314415516c77SSepherosa Ziehau 	}
314515516c77SSepherosa Ziehau 
314615516c77SSepherosa Ziehau 	if (m_head->m_flags & M_VLANTAG) {
314715516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
314815516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_SIZE, NDIS_PKTINFO_TYPE_VLAN);
314915516c77SSepherosa Ziehau 		*pi_data = NDIS_VLAN_INFO_MAKE(
315015516c77SSepherosa Ziehau 		    EVL_VLANOFTAG(m_head->m_pkthdr.ether_vtag),
315115516c77SSepherosa Ziehau 		    EVL_PRIOFTAG(m_head->m_pkthdr.ether_vtag),
315215516c77SSepherosa Ziehau 		    EVL_CFIOFTAG(m_head->m_pkthdr.ether_vtag));
315315516c77SSepherosa Ziehau 	}
315415516c77SSepherosa Ziehau 
315515516c77SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
315615516c77SSepherosa Ziehau #if defined(INET6) || defined(INET)
315715516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
315815516c77SSepherosa Ziehau 		    NDIS_LSO2_INFO_SIZE, NDIS_PKTINFO_TYPE_LSO);
315915516c77SSepherosa Ziehau #ifdef INET
316015516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
3161c49d47daSSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV4(
3162c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen,
316315516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
316415516c77SSepherosa Ziehau 		}
316515516c77SSepherosa Ziehau #endif
316615516c77SSepherosa Ziehau #if defined(INET6) && defined(INET)
316715516c77SSepherosa Ziehau 		else
316815516c77SSepherosa Ziehau #endif
316915516c77SSepherosa Ziehau #ifdef INET6
317015516c77SSepherosa Ziehau 		{
3171c49d47daSSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV6(
3172c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen,
317315516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
317415516c77SSepherosa Ziehau 		}
317515516c77SSepherosa Ziehau #endif
317615516c77SSepherosa Ziehau #endif	/* INET6 || INET */
317715516c77SSepherosa Ziehau 	} else if (m_head->m_pkthdr.csum_flags & txr->hn_csum_assist) {
317815516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
317915516c77SSepherosa Ziehau 		    NDIS_TXCSUM_INFO_SIZE, NDIS_PKTINFO_TYPE_CSUM);
318015516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
318115516c77SSepherosa Ziehau 		    (CSUM_IP6_TCP | CSUM_IP6_UDP)) {
318215516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV6;
318315516c77SSepherosa Ziehau 		} else {
318415516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV4;
318515516c77SSepherosa Ziehau 			if (m_head->m_pkthdr.csum_flags & CSUM_IP)
318615516c77SSepherosa Ziehau 				*pi_data |= NDIS_TXCSUM_INFO_IPCS;
318715516c77SSepherosa Ziehau 		}
318815516c77SSepherosa Ziehau 
3189c49d47daSSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
3190c49d47daSSepherosa Ziehau 		    (CSUM_IP_TCP | CSUM_IP6_TCP)) {
3191c49d47daSSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_MKTCPCS(
3192c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen);
3193c49d47daSSepherosa Ziehau 		} else if (m_head->m_pkthdr.csum_flags &
3194c49d47daSSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP6_UDP)) {
3195c49d47daSSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_MKUDPCS(
3196c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen);
3197c49d47daSSepherosa Ziehau 		}
319815516c77SSepherosa Ziehau 	}
319915516c77SSepherosa Ziehau 
3200dc13fee6SSepherosa Ziehau 	pkt_hlen = pkt->rm_pktinfooffset + pkt->rm_pktinfolen;
32018fe90f73SSepherosa Ziehau 	/* Fixup RNDIS packet message total length */
32028fe90f73SSepherosa Ziehau 	pkt->rm_len += pkt_hlen;
320315516c77SSepherosa Ziehau 	/* Convert RNDIS packet message offsets */
32049130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = hn_rndis_pktmsg_offset(pkt_hlen);
320515516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = hn_rndis_pktmsg_offset(pkt->rm_pktinfooffset);
320615516c77SSepherosa Ziehau 
320715516c77SSepherosa Ziehau 	/*
32088966e5d5SSepherosa Ziehau 	 * Fast path: Chimney sending.
320915516c77SSepherosa Ziehau 	 */
32108966e5d5SSepherosa Ziehau 	if (chim != NULL) {
3211dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tgt_txd = txd;
3212dc13fee6SSepherosa Ziehau 
3213dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL) {
3214dc13fee6SSepherosa Ziehau 			tgt_txd = txr->hn_agg_txd;
3215dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
3216dc13fee6SSepherosa Ziehau 			*m_head0 = NULL;
3217dc13fee6SSepherosa Ziehau #endif
3218dc13fee6SSepherosa Ziehau 		}
3219dc13fee6SSepherosa Ziehau 
3220dc13fee6SSepherosa Ziehau 		KASSERT(pkt == chim,
3221dc13fee6SSepherosa Ziehau 		    ("RNDIS pkt not in chimney sending buffer"));
3222dc13fee6SSepherosa Ziehau 		KASSERT(tgt_txd->chim_index != HN_NVS_CHIM_IDX_INVALID,
3223dc13fee6SSepherosa Ziehau 		    ("chimney sending buffer is not used"));
3224dc13fee6SSepherosa Ziehau 		tgt_txd->chim_size += pkt->rm_len;
322515516c77SSepherosa Ziehau 
32268966e5d5SSepherosa Ziehau 		m_copydata(m_head, 0, m_head->m_pkthdr.len,
3227dc13fee6SSepherosa Ziehau 		    ((uint8_t *)chim) + pkt_hlen);
322815516c77SSepherosa Ziehau 
322915516c77SSepherosa Ziehau 		txr->hn_gpa_cnt = 0;
323015516c77SSepherosa Ziehau 		txr->hn_sendpkt = hn_txpkt_chim;
323115516c77SSepherosa Ziehau 		goto done;
323215516c77SSepherosa Ziehau 	}
3233dc13fee6SSepherosa Ziehau 
3234dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("aggregating sglist txdesc"));
32358966e5d5SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
32368966e5d5SSepherosa Ziehau 	    ("chimney buffer is used"));
32378966e5d5SSepherosa Ziehau 	KASSERT(pkt == txd->rndis_pkt, ("RNDIS pkt not in txdesc"));
323815516c77SSepherosa Ziehau 
323915516c77SSepherosa Ziehau 	error = hn_txdesc_dmamap_load(txr, txd, &m_head, segs, &nsegs);
3240dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
3241a0f49d67SMateusz Guzik 		int freed __diagused;
324215516c77SSepherosa Ziehau 
324315516c77SSepherosa Ziehau 		/*
324415516c77SSepherosa Ziehau 		 * This mbuf is not linked w/ the txd yet, so free it now.
324515516c77SSepherosa Ziehau 		 */
324615516c77SSepherosa Ziehau 		m_freem(m_head);
324715516c77SSepherosa Ziehau 		*m_head0 = NULL;
324815516c77SSepherosa Ziehau 
324915516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
325015516c77SSepherosa Ziehau 		KASSERT(freed != 0,
325115516c77SSepherosa Ziehau 		    ("fail to free txd upon txdma error"));
325215516c77SSepherosa Ziehau 
325315516c77SSepherosa Ziehau 		txr->hn_txdma_failed++;
3254dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
325515516c77SSepherosa Ziehau 		return error;
325615516c77SSepherosa Ziehau 	}
325715516c77SSepherosa Ziehau 	*m_head0 = m_head;
325815516c77SSepherosa Ziehau 
325915516c77SSepherosa Ziehau 	/* +1 RNDIS packet message */
326015516c77SSepherosa Ziehau 	txr->hn_gpa_cnt = nsegs + 1;
326115516c77SSepherosa Ziehau 
326215516c77SSepherosa Ziehau 	/* send packet with page buffer */
326315516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_page = atop(txd->rndis_pkt_paddr);
326415516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_ofs = txd->rndis_pkt_paddr & PAGE_MASK;
3265dc13fee6SSepherosa Ziehau 	txr->hn_gpa[0].gpa_len = pkt_hlen;
326615516c77SSepherosa Ziehau 
326715516c77SSepherosa Ziehau 	/*
326815516c77SSepherosa Ziehau 	 * Fill the page buffers with mbuf info after the page
326915516c77SSepherosa Ziehau 	 * buffer for RNDIS packet message.
327015516c77SSepherosa Ziehau 	 */
327115516c77SSepherosa Ziehau 	for (i = 0; i < nsegs; ++i) {
327215516c77SSepherosa Ziehau 		struct vmbus_gpa *gpa = &txr->hn_gpa[i + 1];
327315516c77SSepherosa Ziehau 
327415516c77SSepherosa Ziehau 		gpa->gpa_page = atop(segs[i].ds_addr);
327515516c77SSepherosa Ziehau 		gpa->gpa_ofs = segs[i].ds_addr & PAGE_MASK;
327615516c77SSepherosa Ziehau 		gpa->gpa_len = segs[i].ds_len;
327715516c77SSepherosa Ziehau 	}
327815516c77SSepherosa Ziehau 
327915516c77SSepherosa Ziehau 	txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
328015516c77SSepherosa Ziehau 	txd->chim_size = 0;
328115516c77SSepherosa Ziehau 	txr->hn_sendpkt = hn_txpkt_sglist;
328215516c77SSepherosa Ziehau done:
328315516c77SSepherosa Ziehau 	txd->m = m_head;
328415516c77SSepherosa Ziehau 
328515516c77SSepherosa Ziehau 	/* Set the completion routine */
328615516c77SSepherosa Ziehau 	hn_nvs_sendctx_init(&txd->send_ctx, hn_txpkt_done, txd);
328715516c77SSepherosa Ziehau 
3288dc13fee6SSepherosa Ziehau 	/* Update temporary stats for later use. */
3289dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts++;
3290dc13fee6SSepherosa Ziehau 	txr->hn_stat_size += m_head->m_pkthdr.len;
3291dc13fee6SSepherosa Ziehau 	if (m_head->m_flags & M_MCAST)
3292dc13fee6SSepherosa Ziehau 		txr->hn_stat_mcasts++;
3293dc13fee6SSepherosa Ziehau 
329415516c77SSepherosa Ziehau 	return 0;
329515516c77SSepherosa Ziehau }
329615516c77SSepherosa Ziehau 
329715516c77SSepherosa Ziehau /*
329815516c77SSepherosa Ziehau  * NOTE:
329915516c77SSepherosa Ziehau  * If this function fails, then txd will be freed, but the mbuf
330015516c77SSepherosa Ziehau  * associated w/ the txd will _not_ be freed.
330115516c77SSepherosa Ziehau  */
330215516c77SSepherosa Ziehau static int
33034db5958aSJustin Hibbits hn_txpkt(if_t ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd)
330415516c77SSepherosa Ziehau {
33058e7d3136SSepherosa Ziehau 	int error, send_failed = 0, has_bpf;
330615516c77SSepherosa Ziehau 
330715516c77SSepherosa Ziehau again:
33084db5958aSJustin Hibbits 	has_bpf = bpf_peers_present(if_getbpf(ifp));
33098e7d3136SSepherosa Ziehau 	if (has_bpf) {
331015516c77SSepherosa Ziehau 		/*
33118e7d3136SSepherosa Ziehau 		 * Make sure that this txd and any aggregated txds are not
33128e7d3136SSepherosa Ziehau 		 * freed before ETHER_BPF_MTAP.
331315516c77SSepherosa Ziehau 		 */
331415516c77SSepherosa Ziehau 		hn_txdesc_hold(txd);
33158e7d3136SSepherosa Ziehau 	}
331615516c77SSepherosa Ziehau 	error = txr->hn_sendpkt(txr, txd);
331715516c77SSepherosa Ziehau 	if (!error) {
33188e7d3136SSepherosa Ziehau 		if (has_bpf) {
3319dc13fee6SSepherosa Ziehau 			const struct hn_txdesc *tmp_txd;
3320dc13fee6SSepherosa Ziehau 
332115516c77SSepherosa Ziehau 			ETHER_BPF_MTAP(ifp, txd->m);
3322dc13fee6SSepherosa Ziehau 			STAILQ_FOREACH(tmp_txd, &txd->agg_list, agg_link)
3323dc13fee6SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, tmp_txd->m);
3324dc13fee6SSepherosa Ziehau 		}
3325dc13fee6SSepherosa Ziehau 
3326dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, txr->hn_stat_pkts);
332723bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
332823bf9e15SSepherosa Ziehau 		if (!hn_use_if_start)
332923bf9e15SSepherosa Ziehau #endif
333023bf9e15SSepherosa Ziehau 		{
333115516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OBYTES,
3332dc13fee6SSepherosa Ziehau 			    txr->hn_stat_size);
3333dc13fee6SSepherosa Ziehau 			if (txr->hn_stat_mcasts != 0) {
3334dc13fee6SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OMCASTS,
3335dc13fee6SSepherosa Ziehau 				    txr->hn_stat_mcasts);
333615516c77SSepherosa Ziehau 			}
3337dc13fee6SSepherosa Ziehau 		}
3338dc13fee6SSepherosa Ziehau 		txr->hn_pkts += txr->hn_stat_pkts;
3339dc13fee6SSepherosa Ziehau 		txr->hn_sends++;
334015516c77SSepherosa Ziehau 	}
33418e7d3136SSepherosa Ziehau 	if (has_bpf)
334215516c77SSepherosa Ziehau 		hn_txdesc_put(txr, txd);
334315516c77SSepherosa Ziehau 
334415516c77SSepherosa Ziehau 	if (__predict_false(error)) {
3345a0f49d67SMateusz Guzik 		int freed __diagused;
334615516c77SSepherosa Ziehau 
334715516c77SSepherosa Ziehau 		/*
334815516c77SSepherosa Ziehau 		 * This should "really rarely" happen.
334915516c77SSepherosa Ziehau 		 *
335015516c77SSepherosa Ziehau 		 * XXX Too many RX to be acked or too many sideband
335115516c77SSepherosa Ziehau 		 * commands to run?  Ask netvsc_channel_rollup()
335215516c77SSepherosa Ziehau 		 * to kick start later.
335315516c77SSepherosa Ziehau 		 */
335415516c77SSepherosa Ziehau 		txr->hn_has_txeof = 1;
335515516c77SSepherosa Ziehau 		if (!send_failed) {
335615516c77SSepherosa Ziehau 			txr->hn_send_failed++;
335715516c77SSepherosa Ziehau 			send_failed = 1;
335815516c77SSepherosa Ziehau 			/*
335915516c77SSepherosa Ziehau 			 * Try sending again after set hn_has_txeof;
336015516c77SSepherosa Ziehau 			 * in case that we missed the last
336115516c77SSepherosa Ziehau 			 * netvsc_channel_rollup().
336215516c77SSepherosa Ziehau 			 */
336315516c77SSepherosa Ziehau 			goto again;
336415516c77SSepherosa Ziehau 		}
336515516c77SSepherosa Ziehau 		if_printf(ifp, "send failed\n");
336615516c77SSepherosa Ziehau 
336715516c77SSepherosa Ziehau 		/*
336815516c77SSepherosa Ziehau 		 * Caller will perform further processing on the
336915516c77SSepherosa Ziehau 		 * associated mbuf, so don't free it in hn_txdesc_put();
337015516c77SSepherosa Ziehau 		 * only unload it from the DMA map in hn_txdesc_put(),
337115516c77SSepherosa Ziehau 		 * if it was loaded.
337215516c77SSepherosa Ziehau 		 */
337315516c77SSepherosa Ziehau 		txd->m = NULL;
337415516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
337515516c77SSepherosa Ziehau 		KASSERT(freed != 0,
337615516c77SSepherosa Ziehau 		    ("fail to free txd upon send error"));
337715516c77SSepherosa Ziehau 
337815516c77SSepherosa Ziehau 		txr->hn_send_failed++;
337915516c77SSepherosa Ziehau 	}
3380dc13fee6SSepherosa Ziehau 
3381dc13fee6SSepherosa Ziehau 	/* Reset temporary stats, after this sending is done. */
3382dc13fee6SSepherosa Ziehau 	txr->hn_stat_size = 0;
3383dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts = 0;
3384dc13fee6SSepherosa Ziehau 	txr->hn_stat_mcasts = 0;
3385dc13fee6SSepherosa Ziehau 
3386dc13fee6SSepherosa Ziehau 	return (error);
338715516c77SSepherosa Ziehau }
338815516c77SSepherosa Ziehau 
338915516c77SSepherosa Ziehau /*
339015516c77SSepherosa Ziehau  * Append the specified data to the indicated mbuf chain,
339115516c77SSepherosa Ziehau  * Extend the mbuf chain if the new data does not fit in
339215516c77SSepherosa Ziehau  * existing space.
339315516c77SSepherosa Ziehau  *
339415516c77SSepherosa Ziehau  * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
339515516c77SSepherosa Ziehau  * There should be an equivalent in the kernel mbuf code,
339615516c77SSepherosa Ziehau  * but there does not appear to be one yet.
339715516c77SSepherosa Ziehau  *
339815516c77SSepherosa Ziehau  * Differs from m_append() in that additional mbufs are
339915516c77SSepherosa Ziehau  * allocated with cluster size MJUMPAGESIZE, and filled
340015516c77SSepherosa Ziehau  * accordingly.
340115516c77SSepherosa Ziehau  *
3402a491581fSWei Hu  * Return the last mbuf in the chain or NULL if failed to
3403a491581fSWei Hu  * allocate new mbuf.
340415516c77SSepherosa Ziehau  */
3405a491581fSWei Hu static struct mbuf *
340615516c77SSepherosa Ziehau hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
340715516c77SSepherosa Ziehau {
340815516c77SSepherosa Ziehau 	struct mbuf *m, *n;
340915516c77SSepherosa Ziehau 	int remainder, space;
341015516c77SSepherosa Ziehau 
341115516c77SSepherosa Ziehau 	for (m = m0; m->m_next != NULL; m = m->m_next)
341215516c77SSepherosa Ziehau 		;
341315516c77SSepherosa Ziehau 	remainder = len;
341415516c77SSepherosa Ziehau 	space = M_TRAILINGSPACE(m);
341515516c77SSepherosa Ziehau 	if (space > 0) {
341615516c77SSepherosa Ziehau 		/*
341715516c77SSepherosa Ziehau 		 * Copy into available space.
341815516c77SSepherosa Ziehau 		 */
341915516c77SSepherosa Ziehau 		if (space > remainder)
342015516c77SSepherosa Ziehau 			space = remainder;
342115516c77SSepherosa Ziehau 		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
342215516c77SSepherosa Ziehau 		m->m_len += space;
342315516c77SSepherosa Ziehau 		cp += space;
342415516c77SSepherosa Ziehau 		remainder -= space;
342515516c77SSepherosa Ziehau 	}
342615516c77SSepherosa Ziehau 	while (remainder > 0) {
342715516c77SSepherosa Ziehau 		/*
342815516c77SSepherosa Ziehau 		 * Allocate a new mbuf; could check space
342915516c77SSepherosa Ziehau 		 * and allocate a cluster instead.
343015516c77SSepherosa Ziehau 		 */
343115516c77SSepherosa Ziehau 		n = m_getjcl(M_NOWAIT, m->m_type, 0, MJUMPAGESIZE);
343215516c77SSepherosa Ziehau 		if (n == NULL)
3433a491581fSWei Hu 			return NULL;
343415516c77SSepherosa Ziehau 		n->m_len = min(MJUMPAGESIZE, remainder);
343515516c77SSepherosa Ziehau 		bcopy(cp, mtod(n, caddr_t), n->m_len);
343615516c77SSepherosa Ziehau 		cp += n->m_len;
343715516c77SSepherosa Ziehau 		remainder -= n->m_len;
343815516c77SSepherosa Ziehau 		m->m_next = n;
343915516c77SSepherosa Ziehau 		m = n;
344015516c77SSepherosa Ziehau 	}
344115516c77SSepherosa Ziehau 
3442a491581fSWei Hu 	return m;
344315516c77SSepherosa Ziehau }
344415516c77SSepherosa Ziehau 
344515516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
344615516c77SSepherosa Ziehau static __inline int
344715516c77SSepherosa Ziehau hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m)
344815516c77SSepherosa Ziehau {
344915516c77SSepherosa Ziehau 	if (hn_lro_mbufq_depth) {
345015516c77SSepherosa Ziehau 		tcp_lro_queue_mbuf(lc, m);
345115516c77SSepherosa Ziehau 		return 0;
345215516c77SSepherosa Ziehau 	}
345315516c77SSepherosa Ziehau 	return tcp_lro_rx(lc, m, 0);
345415516c77SSepherosa Ziehau }
345515516c77SSepherosa Ziehau #endif
345615516c77SSepherosa Ziehau 
345715516c77SSepherosa Ziehau static int
3458a491581fSWei Hu hn_rxpkt(struct hn_rx_ring *rxr)
345915516c77SSepherosa Ziehau {
34604db5958aSJustin Hibbits 	if_t ifp, hn_ifp = rxr->hn_ifp;
3461a491581fSWei Hu 	struct mbuf *m_new, *n;
3462642ec226SSepherosa Ziehau 	int size, do_lro = 0, do_csum = 1, is_vf = 0;
3463642ec226SSepherosa Ziehau 	int hash_type = M_HASHTYPE_NONE;
3464db76829bSSepherosa Ziehau 	int l3proto = ETHERTYPE_MAX, l4proto = IPPROTO_DONE;
3465a491581fSWei Hu 	int i;
346615516c77SSepherosa Ziehau 
3467642ec226SSepherosa Ziehau 	ifp = hn_ifp;
3468642ec226SSepherosa Ziehau 	if (rxr->hn_rxvf_ifp != NULL) {
3469a97fff19SSepherosa Ziehau 		/*
3470642ec226SSepherosa Ziehau 		 * Non-transparent mode VF; pretend this packet is from
3471642ec226SSepherosa Ziehau 		 * the VF.
3472a97fff19SSepherosa Ziehau 		 */
3473642ec226SSepherosa Ziehau 		ifp = rxr->hn_rxvf_ifp;
3474642ec226SSepherosa Ziehau 		is_vf = 1;
3475642ec226SSepherosa Ziehau 	} else if (rxr->hn_rx_flags & HN_RX_FLAG_XPNT_VF) {
3476642ec226SSepherosa Ziehau 		/* Transparent mode VF. */
3477642ec226SSepherosa Ziehau 		is_vf = 1;
3478642ec226SSepherosa Ziehau 	}
34795bdfd3fdSDexuan Cui 
34804db5958aSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
3481b3b75d9cSSepherosa Ziehau 		/*
3482b3b75d9cSSepherosa Ziehau 		 * NOTE:
3483b3b75d9cSSepherosa Ziehau 		 * See the NOTE of hn_rndis_init_fixat().  This
3484b3b75d9cSSepherosa Ziehau 		 * function can be reached, immediately after the
3485b3b75d9cSSepherosa Ziehau 		 * RNDIS is initialized but before the ifnet is
3486b3b75d9cSSepherosa Ziehau 		 * setup on the hn_attach() path; drop the unexpected
3487b3b75d9cSSepherosa Ziehau 		 * packets.
3488b3b75d9cSSepherosa Ziehau 		 */
3489b3b75d9cSSepherosa Ziehau 		return (0);
3490b3b75d9cSSepherosa Ziehau 	}
3491b3b75d9cSSepherosa Ziehau 
3492a491581fSWei Hu 	if (__predict_false(rxr->rsc.pktlen < ETHER_HDR_LEN)) {
3493a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IERRORS, 1);
3494a97fff19SSepherosa Ziehau 		return (0);
3495a97fff19SSepherosa Ziehau 	}
3496a97fff19SSepherosa Ziehau 
3497a491581fSWei Hu 	if (rxr->rsc.cnt == 1 && rxr->rsc.pktlen <= MHLEN) {
349815516c77SSepherosa Ziehau 		m_new = m_gethdr(M_NOWAIT, MT_DATA);
349915516c77SSepherosa Ziehau 		if (m_new == NULL) {
3500a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
350115516c77SSepherosa Ziehau 			return (0);
350215516c77SSepherosa Ziehau 		}
3503a491581fSWei Hu 		memcpy(mtod(m_new, void *), rxr->rsc.frag_data[0],
3504a491581fSWei Hu 		    rxr->rsc.frag_len[0]);
3505a491581fSWei Hu 		m_new->m_pkthdr.len = m_new->m_len = rxr->rsc.frag_len[0];
350615516c77SSepherosa Ziehau 	} else {
350715516c77SSepherosa Ziehau 		/*
350815516c77SSepherosa Ziehau 		 * Get an mbuf with a cluster.  For packets 2K or less,
350915516c77SSepherosa Ziehau 		 * get a standard 2K cluster.  For anything larger, get a
351015516c77SSepherosa Ziehau 		 * 4K cluster.  Any buffers larger than 4K can cause problems
351115516c77SSepherosa Ziehau 		 * if looped around to the Hyper-V TX channel, so avoid them.
351215516c77SSepherosa Ziehau 		 */
351315516c77SSepherosa Ziehau 		size = MCLBYTES;
3514a491581fSWei Hu 		if (rxr->rsc.pktlen > MCLBYTES) {
351515516c77SSepherosa Ziehau 			/* 4096 */
351615516c77SSepherosa Ziehau 			size = MJUMPAGESIZE;
351715516c77SSepherosa Ziehau 		}
351815516c77SSepherosa Ziehau 
351915516c77SSepherosa Ziehau 		m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size);
352015516c77SSepherosa Ziehau 		if (m_new == NULL) {
3521a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
352215516c77SSepherosa Ziehau 			return (0);
352315516c77SSepherosa Ziehau 		}
352415516c77SSepherosa Ziehau 
3525a491581fSWei Hu 		n = m_new;
3526a491581fSWei Hu 		for (i = 0; i < rxr->rsc.cnt; i++) {
3527a491581fSWei Hu 			n = hv_m_append(n, rxr->rsc.frag_len[i],
3528a491581fSWei Hu 			    rxr->rsc.frag_data[i]);
3529a491581fSWei Hu 			if (n == NULL) {
3530a491581fSWei Hu 				if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
3531a491581fSWei Hu 				return (0);
3532a491581fSWei Hu 			} else {
3533a491581fSWei Hu 				m_new->m_pkthdr.len += rxr->rsc.frag_len[i];
353415516c77SSepherosa Ziehau 			}
3535a491581fSWei Hu 		}
3536a491581fSWei Hu 	}
3537a491581fSWei Hu 	if (rxr->rsc.pktlen <= MHLEN)
3538a491581fSWei Hu 		rxr->hn_small_pkts++;
3539a491581fSWei Hu 
354015516c77SSepherosa Ziehau 	m_new->m_pkthdr.rcvif = ifp;
354115516c77SSepherosa Ziehau 
35424db5958aSJustin Hibbits 	if (__predict_false((if_getcapenable(hn_ifp) & IFCAP_RXCSUM) == 0))
354315516c77SSepherosa Ziehau 		do_csum = 0;
354415516c77SSepherosa Ziehau 
354515516c77SSepherosa Ziehau 	/* receive side checksum offload */
3546a491581fSWei Hu 	if (rxr->rsc.csum_info != NULL) {
354715516c77SSepherosa Ziehau 		/* IP csum offload */
3548a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) & NDIS_RXCSUM_INFO_IPCS_OK) && do_csum) {
354915516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
355015516c77SSepherosa Ziehau 			    (CSUM_IP_CHECKED | CSUM_IP_VALID);
355115516c77SSepherosa Ziehau 			rxr->hn_csum_ip++;
355215516c77SSepherosa Ziehau 		}
355315516c77SSepherosa Ziehau 
355415516c77SSepherosa Ziehau 		/* TCP/UDP csum offload */
3555a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) & (NDIS_RXCSUM_INFO_UDPCS_OK |
355615516c77SSepherosa Ziehau 		     NDIS_RXCSUM_INFO_TCPCS_OK)) && do_csum) {
355715516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
355815516c77SSepherosa Ziehau 			    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
355915516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_data = 0xffff;
3560a491581fSWei Hu 			if (*(rxr->rsc.csum_info) & NDIS_RXCSUM_INFO_TCPCS_OK)
356115516c77SSepherosa Ziehau 				rxr->hn_csum_tcp++;
356215516c77SSepherosa Ziehau 			else
356315516c77SSepherosa Ziehau 				rxr->hn_csum_udp++;
356415516c77SSepherosa Ziehau 		}
356515516c77SSepherosa Ziehau 
356615516c77SSepherosa Ziehau 		/*
356715516c77SSepherosa Ziehau 		 * XXX
356815516c77SSepherosa Ziehau 		 * As of this write (Oct 28th, 2016), host side will turn
356915516c77SSepherosa Ziehau 		 * on only TCPCS_OK and IPCS_OK even for UDP datagrams, so
357015516c77SSepherosa Ziehau 		 * the do_lro setting here is actually _not_ accurate.  We
357115516c77SSepherosa Ziehau 		 * depend on the RSS hash type check to reset do_lro.
357215516c77SSepherosa Ziehau 		 */
3573a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) &
357415516c77SSepherosa Ziehau 		     (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) ==
357515516c77SSepherosa Ziehau 		    (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK))
357615516c77SSepherosa Ziehau 			do_lro = 1;
357715516c77SSepherosa Ziehau 	} else {
3578db76829bSSepherosa Ziehau 		hn_rxpkt_proto(m_new, &l3proto, &l4proto);
3579db76829bSSepherosa Ziehau 		if (l3proto == ETHERTYPE_IP) {
3580db76829bSSepherosa Ziehau 			if (l4proto == IPPROTO_TCP) {
358115516c77SSepherosa Ziehau 				if (do_csum &&
358215516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
358315516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_TCP)) {
358415516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
358515516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
358615516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
358715516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
358815516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
358915516c77SSepherosa Ziehau 				}
359015516c77SSepherosa Ziehau 				do_lro = 1;
3591db76829bSSepherosa Ziehau 			} else if (l4proto == IPPROTO_UDP) {
359215516c77SSepherosa Ziehau 				if (do_csum &&
359315516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
359415516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_UDP)) {
359515516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
359615516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
359715516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
359815516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
359915516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
360015516c77SSepherosa Ziehau 				}
3601db76829bSSepherosa Ziehau 			} else if (l4proto != IPPROTO_DONE && do_csum &&
360215516c77SSepherosa Ziehau 			    (rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) {
360315516c77SSepherosa Ziehau 				rxr->hn_csum_trusted++;
360415516c77SSepherosa Ziehau 				m_new->m_pkthdr.csum_flags |=
360515516c77SSepherosa Ziehau 				    (CSUM_IP_CHECKED | CSUM_IP_VALID);
360615516c77SSepherosa Ziehau 			}
360715516c77SSepherosa Ziehau 		}
360815516c77SSepherosa Ziehau 	}
3609db76829bSSepherosa Ziehau 
3610a491581fSWei Hu 	if (rxr->rsc.vlan_info != NULL) {
361115516c77SSepherosa Ziehau 		m_new->m_pkthdr.ether_vtag = EVL_MAKETAG(
3612a491581fSWei Hu 		    NDIS_VLAN_INFO_ID(*(rxr->rsc.vlan_info)),
3613a491581fSWei Hu 		    NDIS_VLAN_INFO_PRI(*(rxr->rsc.vlan_info)),
3614a491581fSWei Hu 		    NDIS_VLAN_INFO_CFI(*(rxr->rsc.vlan_info)));
361515516c77SSepherosa Ziehau 		m_new->m_flags |= M_VLANTAG;
361615516c77SSepherosa Ziehau 	}
361715516c77SSepherosa Ziehau 
3618a97fff19SSepherosa Ziehau 	/*
3619a97fff19SSepherosa Ziehau 	 * If VF is activated (tranparent/non-transparent mode does not
3620a97fff19SSepherosa Ziehau 	 * matter here).
3621a97fff19SSepherosa Ziehau 	 *
3622a97fff19SSepherosa Ziehau 	 * - Disable LRO
3623a97fff19SSepherosa Ziehau 	 *
3624a97fff19SSepherosa Ziehau 	 *   hn(4) will only receive broadcast packets, multicast packets,
3625a97fff19SSepherosa Ziehau 	 *   TCP SYN and SYN|ACK (in Azure), LRO is useless for these
3626a97fff19SSepherosa Ziehau 	 *   packet types.
3627a97fff19SSepherosa Ziehau 	 *
3628a97fff19SSepherosa Ziehau 	 *   For non-transparent, we definitely _cannot_ enable LRO at
3629a97fff19SSepherosa Ziehau 	 *   all, since the LRO flush will use hn(4) as the receiving
3630a97fff19SSepherosa Ziehau 	 *   interface; i.e. hn_ifp->if_input(hn_ifp, m).
3631a97fff19SSepherosa Ziehau 	 */
3632642ec226SSepherosa Ziehau 	if (is_vf)
3633642ec226SSepherosa Ziehau 		do_lro = 0;
3634a97fff19SSepherosa Ziehau 
3635642ec226SSepherosa Ziehau 	/*
3636642ec226SSepherosa Ziehau 	 * If VF is activated (tranparent/non-transparent mode does not
3637642ec226SSepherosa Ziehau 	 * matter here), do _not_ mess with unsupported hash types or
3638642ec226SSepherosa Ziehau 	 * functions.
3639642ec226SSepherosa Ziehau 	 */
3640a491581fSWei Hu 	if (rxr->rsc.hash_info != NULL) {
364115516c77SSepherosa Ziehau 		rxr->hn_rss_pkts++;
3642a491581fSWei Hu 		m_new->m_pkthdr.flowid = *(rxr->rsc.hash_value);
3643642ec226SSepherosa Ziehau 		if (!is_vf)
364415516c77SSepherosa Ziehau 			hash_type = M_HASHTYPE_OPAQUE_HASH;
3645a491581fSWei Hu 		if ((*(rxr->rsc.hash_info) & NDIS_HASH_FUNCTION_MASK) ==
364615516c77SSepherosa Ziehau 		    NDIS_HASH_FUNCTION_TOEPLITZ) {
3647a491581fSWei Hu 			uint32_t type = (*(rxr->rsc.hash_info) & NDIS_HASH_TYPE_MASK &
3648642ec226SSepherosa Ziehau 			    rxr->hn_mbuf_hash);
364915516c77SSepherosa Ziehau 
365015516c77SSepherosa Ziehau 			/*
365115516c77SSepherosa Ziehau 			 * NOTE:
365215516c77SSepherosa Ziehau 			 * do_lro is resetted, if the hash types are not TCP
365315516c77SSepherosa Ziehau 			 * related.  See the comment in the above csum_flags
365415516c77SSepherosa Ziehau 			 * setup section.
365515516c77SSepherosa Ziehau 			 */
365615516c77SSepherosa Ziehau 			switch (type) {
365715516c77SSepherosa Ziehau 			case NDIS_HASH_IPV4:
365815516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV4;
365915516c77SSepherosa Ziehau 				do_lro = 0;
366015516c77SSepherosa Ziehau 				break;
366115516c77SSepherosa Ziehau 
366215516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV4:
366315516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV4;
3664db76829bSSepherosa Ziehau 				if (rxr->hn_rx_flags & HN_RX_FLAG_UDP_HASH) {
3665db76829bSSepherosa Ziehau 					int def_htype = M_HASHTYPE_OPAQUE_HASH;
3666db76829bSSepherosa Ziehau 
3667db76829bSSepherosa Ziehau 					if (is_vf)
3668db76829bSSepherosa Ziehau 						def_htype = M_HASHTYPE_NONE;
3669db76829bSSepherosa Ziehau 
3670db76829bSSepherosa Ziehau 					/*
3671db76829bSSepherosa Ziehau 					 * UDP 4-tuple hash is delivered as
3672db76829bSSepherosa Ziehau 					 * TCP 4-tuple hash.
3673db76829bSSepherosa Ziehau 					 */
3674db76829bSSepherosa Ziehau 					if (l3proto == ETHERTYPE_MAX) {
3675db76829bSSepherosa Ziehau 						hn_rxpkt_proto(m_new,
3676db76829bSSepherosa Ziehau 						    &l3proto, &l4proto);
3677db76829bSSepherosa Ziehau 					}
3678db76829bSSepherosa Ziehau 					if (l3proto == ETHERTYPE_IP) {
36796f12c42eSSepherosa Ziehau 						if (l4proto == IPPROTO_UDP &&
36806f12c42eSSepherosa Ziehau 						    (rxr->hn_mbuf_hash &
36816f12c42eSSepherosa Ziehau 						     NDIS_HASH_UDP_IPV4_X)) {
3682db76829bSSepherosa Ziehau 							hash_type =
3683db76829bSSepherosa Ziehau 							M_HASHTYPE_RSS_UDP_IPV4;
3684db76829bSSepherosa Ziehau 							do_lro = 0;
3685db76829bSSepherosa Ziehau 						} else if (l4proto !=
3686db76829bSSepherosa Ziehau 						    IPPROTO_TCP) {
3687db76829bSSepherosa Ziehau 							hash_type = def_htype;
3688db76829bSSepherosa Ziehau 							do_lro = 0;
3689db76829bSSepherosa Ziehau 						}
3690db76829bSSepherosa Ziehau 					} else {
3691db76829bSSepherosa Ziehau 						hash_type = def_htype;
3692db76829bSSepherosa Ziehau 						do_lro = 0;
3693db76829bSSepherosa Ziehau 					}
3694db76829bSSepherosa Ziehau 				}
369515516c77SSepherosa Ziehau 				break;
369615516c77SSepherosa Ziehau 
369715516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6:
369815516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6;
369915516c77SSepherosa Ziehau 				do_lro = 0;
370015516c77SSepherosa Ziehau 				break;
370115516c77SSepherosa Ziehau 
370215516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6_EX:
370315516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6_EX;
370415516c77SSepherosa Ziehau 				do_lro = 0;
370515516c77SSepherosa Ziehau 				break;
370615516c77SSepherosa Ziehau 
370715516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6:
370815516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6;
370915516c77SSepherosa Ziehau 				break;
371015516c77SSepherosa Ziehau 
371115516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6_EX:
371215516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6_EX;
371315516c77SSepherosa Ziehau 				break;
371415516c77SSepherosa Ziehau 			}
371515516c77SSepherosa Ziehau 		}
3716642ec226SSepherosa Ziehau 	} else if (!is_vf) {
371715516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = rxr->hn_rx_idx;
371815516c77SSepherosa Ziehau 		hash_type = M_HASHTYPE_OPAQUE;
371915516c77SSepherosa Ziehau 	}
372015516c77SSepherosa Ziehau 	M_HASHTYPE_SET(m_new, hash_type);
372115516c77SSepherosa Ziehau 
3722a97fff19SSepherosa Ziehau 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
3723a97fff19SSepherosa Ziehau 	if (hn_ifp != ifp) {
3724a97fff19SSepherosa Ziehau 		const struct ether_header *eh;
3725a97fff19SSepherosa Ziehau 
372615516c77SSepherosa Ziehau 		/*
3727a97fff19SSepherosa Ziehau 		 * Non-transparent mode VF is activated.
372815516c77SSepherosa Ziehau 		 */
372915516c77SSepherosa Ziehau 
3730a97fff19SSepherosa Ziehau 		/*
3731a97fff19SSepherosa Ziehau 		 * Allow tapping on hn(4).
3732a97fff19SSepherosa Ziehau 		 */
3733a97fff19SSepherosa Ziehau 		ETHER_BPF_MTAP(hn_ifp, m_new);
3734a97fff19SSepherosa Ziehau 
3735a97fff19SSepherosa Ziehau 		/*
3736a97fff19SSepherosa Ziehau 		 * Update hn(4)'s stats.
3737a97fff19SSepherosa Ziehau 		 */
3738a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
3739a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IBYTES, m_new->m_pkthdr.len);
3740a97fff19SSepherosa Ziehau 		/* Checked at the beginning of this function. */
3741a97fff19SSepherosa Ziehau 		KASSERT(m_new->m_len >= ETHER_HDR_LEN, ("not ethernet frame"));
3742a97fff19SSepherosa Ziehau 		eh = mtod(m_new, struct ether_header *);
3743a97fff19SSepherosa Ziehau 		if (ETHER_IS_MULTICAST(eh->ether_dhost))
3744a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IMCASTS, 1);
3745a97fff19SSepherosa Ziehau 	}
374615516c77SSepherosa Ziehau 	rxr->hn_pkts++;
374715516c77SSepherosa Ziehau 
37484db5958aSJustin Hibbits 	if ((if_getcapenable(hn_ifp) & IFCAP_LRO) && do_lro) {
374915516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
375015516c77SSepherosa Ziehau 		struct lro_ctrl *lro = &rxr->hn_lro;
375115516c77SSepherosa Ziehau 
375215516c77SSepherosa Ziehau 		if (lro->lro_cnt) {
375315516c77SSepherosa Ziehau 			rxr->hn_lro_tried++;
375415516c77SSepherosa Ziehau 			if (hn_lro_rx(lro, m_new) == 0) {
375515516c77SSepherosa Ziehau 				/* DONE! */
375615516c77SSepherosa Ziehau 				return 0;
375715516c77SSepherosa Ziehau 			}
375815516c77SSepherosa Ziehau 		}
375915516c77SSepherosa Ziehau #endif
376015516c77SSepherosa Ziehau 	}
37614db5958aSJustin Hibbits 	if_input(ifp, m_new);
376215516c77SSepherosa Ziehau 
376315516c77SSepherosa Ziehau 	return (0);
376415516c77SSepherosa Ziehau }
376515516c77SSepherosa Ziehau 
376615516c77SSepherosa Ziehau static int
37674db5958aSJustin Hibbits hn_ioctl(if_t ifp, u_long cmd, caddr_t data)
376815516c77SSepherosa Ziehau {
37694db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
37709c6cae24SSepherosa Ziehau 	struct ifreq *ifr = (struct ifreq *)data, ifr_vf;
37714db5958aSJustin Hibbits 	if_t vf_ifp;
377215516c77SSepherosa Ziehau 	int mask, error = 0;
37738c068aa5SSepherosa Ziehau 	struct ifrsskey *ifrk;
37748c068aa5SSepherosa Ziehau 	struct ifrsshash *ifrh;
3775eb2fe044SSepherosa Ziehau 	uint32_t mtu;
377615516c77SSepherosa Ziehau 
377715516c77SSepherosa Ziehau 	switch (cmd) {
377815516c77SSepherosa Ziehau 	case SIOCSIFMTU:
377915516c77SSepherosa Ziehau 		if (ifr->ifr_mtu > HN_MTU_MAX) {
378015516c77SSepherosa Ziehau 			error = EINVAL;
378115516c77SSepherosa Ziehau 			break;
378215516c77SSepherosa Ziehau 		}
378315516c77SSepherosa Ziehau 
378415516c77SSepherosa Ziehau 		HN_LOCK(sc);
378515516c77SSepherosa Ziehau 
378615516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
378715516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
378815516c77SSepherosa Ziehau 			break;
378915516c77SSepherosa Ziehau 		}
379015516c77SSepherosa Ziehau 
379115516c77SSepherosa Ziehau 		if ((sc->hn_caps & HN_CAP_MTU) == 0) {
379215516c77SSepherosa Ziehau 			/* Can't change MTU */
379315516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
379415516c77SSepherosa Ziehau 			error = EOPNOTSUPP;
379515516c77SSepherosa Ziehau 			break;
379615516c77SSepherosa Ziehau 		}
379715516c77SSepherosa Ziehau 
37984db5958aSJustin Hibbits 		if (if_getmtu(ifp) == ifr->ifr_mtu) {
379915516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
380015516c77SSepherosa Ziehau 			break;
380115516c77SSepherosa Ziehau 		}
380215516c77SSepherosa Ziehau 
38039c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
38049c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
38059c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
38064db5958aSJustin Hibbits 			strlcpy(ifr_vf.ifr_name, if_name(vf_ifp),
38079c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
38084db5958aSJustin Hibbits 			error = ifhwioctl(SIOCSIFMTU,vf_ifp,
38094db5958aSJustin Hibbits 			    (caddr_t)&ifr_vf, curthread);
38109c6cae24SSepherosa Ziehau 			if (error) {
38119c6cae24SSepherosa Ziehau 				HN_UNLOCK(sc);
38129c6cae24SSepherosa Ziehau 				if_printf(ifp, "%s SIOCSIFMTU %d failed: %d\n",
38134db5958aSJustin Hibbits 				    if_name(vf_ifp), ifr->ifr_mtu, error);
38149c6cae24SSepherosa Ziehau 				break;
38159c6cae24SSepherosa Ziehau 			}
38169c6cae24SSepherosa Ziehau 		}
38179c6cae24SSepherosa Ziehau 
381815516c77SSepherosa Ziehau 		/*
381915516c77SSepherosa Ziehau 		 * Suspend this interface before the synthetic parts
382015516c77SSepherosa Ziehau 		 * are ripped.
382115516c77SSepherosa Ziehau 		 */
382215516c77SSepherosa Ziehau 		hn_suspend(sc);
382315516c77SSepherosa Ziehau 
382415516c77SSepherosa Ziehau 		/*
382515516c77SSepherosa Ziehau 		 * Detach the synthetics parts, i.e. NVS and RNDIS.
382615516c77SSepherosa Ziehau 		 */
382715516c77SSepherosa Ziehau 		hn_synth_detach(sc);
382815516c77SSepherosa Ziehau 
382915516c77SSepherosa Ziehau 		/*
383015516c77SSepherosa Ziehau 		 * Reattach the synthetic parts, i.e. NVS and RNDIS,
383115516c77SSepherosa Ziehau 		 * with the new MTU setting.
383215516c77SSepherosa Ziehau 		 */
383315516c77SSepherosa Ziehau 		error = hn_synth_attach(sc, ifr->ifr_mtu);
383415516c77SSepherosa Ziehau 		if (error) {
383515516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
383615516c77SSepherosa Ziehau 			break;
383715516c77SSepherosa Ziehau 		}
383815516c77SSepherosa Ziehau 
3839eb2fe044SSepherosa Ziehau 		error = hn_rndis_get_mtu(sc, &mtu);
3840eb2fe044SSepherosa Ziehau 		if (error)
3841eb2fe044SSepherosa Ziehau 			mtu = ifr->ifr_mtu;
3842eb2fe044SSepherosa Ziehau 		else if (bootverbose)
3843eb2fe044SSepherosa Ziehau 			if_printf(ifp, "RNDIS mtu %u\n", mtu);
3844eb2fe044SSepherosa Ziehau 
384515516c77SSepherosa Ziehau 		/*
384615516c77SSepherosa Ziehau 		 * Commit the requested MTU, after the synthetic parts
384715516c77SSepherosa Ziehau 		 * have been successfully attached.
384815516c77SSepherosa Ziehau 		 */
3849eb2fe044SSepherosa Ziehau 		if (mtu >= ifr->ifr_mtu) {
3850eb2fe044SSepherosa Ziehau 			mtu = ifr->ifr_mtu;
3851eb2fe044SSepherosa Ziehau 		} else {
3852eb2fe044SSepherosa Ziehau 			if_printf(ifp, "fixup mtu %d -> %u\n",
3853eb2fe044SSepherosa Ziehau 			    ifr->ifr_mtu, mtu);
3854eb2fe044SSepherosa Ziehau 		}
38554db5958aSJustin Hibbits 		if_setmtu(ifp, mtu);
385615516c77SSepherosa Ziehau 
385715516c77SSepherosa Ziehau 		/*
38589c6cae24SSepherosa Ziehau 		 * Synthetic parts' reattach may change the chimney
38599c6cae24SSepherosa Ziehau 		 * sending size; update it.
386015516c77SSepherosa Ziehau 		 */
386115516c77SSepherosa Ziehau 		if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
386215516c77SSepherosa Ziehau 			hn_set_chim_size(sc, sc->hn_chim_szmax);
38639c6cae24SSepherosa Ziehau 
38649c6cae24SSepherosa Ziehau 		/*
38659c6cae24SSepherosa Ziehau 		 * Make sure that various parameters based on MTU are
38669c6cae24SSepherosa Ziehau 		 * still valid, after the MTU change.
38679c6cae24SSepherosa Ziehau 		 */
38689c6cae24SSepherosa Ziehau 		hn_mtu_change_fixup(sc);
386915516c77SSepherosa Ziehau 
387015516c77SSepherosa Ziehau 		/*
387115516c77SSepherosa Ziehau 		 * All done!  Resume the interface now.
387215516c77SSepherosa Ziehau 		 */
387315516c77SSepherosa Ziehau 		hn_resume(sc);
387415516c77SSepherosa Ziehau 
3875d0cd8231SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXVF) ||
3876d0cd8231SSepherosa Ziehau 		    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
38779c6cae24SSepherosa Ziehau 			/*
38789c6cae24SSepherosa Ziehau 			 * Since we have reattached the NVS part,
38799c6cae24SSepherosa Ziehau 			 * change the datapath to VF again; in case
38809c6cae24SSepherosa Ziehau 			 * that it is lost, after the NVS was detached.
38819c6cae24SSepherosa Ziehau 			 */
38829c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
38839c6cae24SSepherosa Ziehau 		}
38849c6cae24SSepherosa Ziehau 
388515516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
388615516c77SSepherosa Ziehau 		break;
388715516c77SSepherosa Ziehau 
388815516c77SSepherosa Ziehau 	case SIOCSIFFLAGS:
388915516c77SSepherosa Ziehau 		HN_LOCK(sc);
389015516c77SSepherosa Ziehau 
389115516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
389215516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
389315516c77SSepherosa Ziehau 			break;
389415516c77SSepherosa Ziehau 		}
389515516c77SSepherosa Ziehau 
38969c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc))
38979c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
38989c6cae24SSepherosa Ziehau 
38994db5958aSJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
39004db5958aSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
3901fdc4f478SSepherosa Ziehau 				/*
3902fdc4f478SSepherosa Ziehau 				 * Caller meight hold mutex, e.g.
3903fdc4f478SSepherosa Ziehau 				 * bpf; use busy-wait for the RNDIS
3904fdc4f478SSepherosa Ziehau 				 * reply.
3905fdc4f478SSepherosa Ziehau 				 */
3906fdc4f478SSepherosa Ziehau 				HN_NO_SLEEPING(sc);
3907c08f7b2cSSepherosa Ziehau 				hn_rxfilter_config(sc);
3908fdc4f478SSepherosa Ziehau 				HN_SLEEPING_OK(sc);
39099c6cae24SSepherosa Ziehau 
39109c6cae24SSepherosa Ziehau 				if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
39119c6cae24SSepherosa Ziehau 					error = hn_xpnt_vf_iocsetflags(sc);
3912fdc4f478SSepherosa Ziehau 			} else {
391315516c77SSepherosa Ziehau 				hn_init_locked(sc);
3914fdc4f478SSepherosa Ziehau 			}
391515516c77SSepherosa Ziehau 		} else {
39164db5958aSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
39175bdfd3fdSDexuan Cui 				hn_stop(sc, false);
391815516c77SSepherosa Ziehau 		}
39194db5958aSJustin Hibbits 		sc->hn_if_flags = if_getflags(ifp);
392015516c77SSepherosa Ziehau 
392115516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
392215516c77SSepherosa Ziehau 		break;
392315516c77SSepherosa Ziehau 
392415516c77SSepherosa Ziehau 	case SIOCSIFCAP:
392515516c77SSepherosa Ziehau 		HN_LOCK(sc);
39269c6cae24SSepherosa Ziehau 
39279c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
39289c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
39294db5958aSJustin Hibbits 			strlcpy(ifr_vf.ifr_name, if_name(sc->hn_vf_ifp),
39309c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
39319c6cae24SSepherosa Ziehau 			error = hn_xpnt_vf_iocsetcaps(sc, &ifr_vf);
39329c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
39339c6cae24SSepherosa Ziehau 			break;
39349c6cae24SSepherosa Ziehau 		}
39359c6cae24SSepherosa Ziehau 
39369c6cae24SSepherosa Ziehau 		/*
39379c6cae24SSepherosa Ziehau 		 * Fix up requested capabilities w/ supported capabilities,
39389c6cae24SSepherosa Ziehau 		 * since the supported capabilities could have been changed.
39399c6cae24SSepherosa Ziehau 		 */
39404db5958aSJustin Hibbits 		mask = (ifr->ifr_reqcap & if_getcapabilities(ifp)) ^
39414db5958aSJustin Hibbits 		    if_getcapenable(ifp);
394215516c77SSepherosa Ziehau 
394315516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM) {
39444db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TXCSUM);
39454db5958aSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TXCSUM)
39464db5958aSJustin Hibbits 				if_sethwassistbits(ifp, HN_CSUM_IP_HWASSIST(sc), 0);
394715516c77SSepherosa Ziehau 			else
39484db5958aSJustin Hibbits 				if_sethwassistbits(ifp, 0, HN_CSUM_IP_HWASSIST(sc));
394915516c77SSepherosa Ziehau 		}
395015516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM_IPV6) {
39514db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TXCSUM_IPV6);
39524db5958aSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6)
39534db5958aSJustin Hibbits 				if_sethwassistbits(ifp, HN_CSUM_IP6_HWASSIST(sc), 0);
395415516c77SSepherosa Ziehau 			else
39554db5958aSJustin Hibbits 				if_sethwassistbits(ifp, 0, HN_CSUM_IP6_HWASSIST(sc));
395615516c77SSepherosa Ziehau 		}
395715516c77SSepherosa Ziehau 
395815516c77SSepherosa Ziehau 		/* TODO: flip RNDIS offload parameters for RXCSUM. */
395915516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
39604db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_RXCSUM);
396115516c77SSepherosa Ziehau #ifdef foo
396215516c77SSepherosa Ziehau 		/* We can't diff IPv6 packets from IPv4 packets on RX path. */
396315516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM_IPV6)
39644db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_RXCSUM_IPV6);
396515516c77SSepherosa Ziehau #endif
396615516c77SSepherosa Ziehau 
396715516c77SSepherosa Ziehau 		if (mask & IFCAP_LRO)
39684db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_LRO);
396915516c77SSepherosa Ziehau 
397015516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO4) {
39714db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TSO4);
39724db5958aSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TSO4)
39734db5958aSJustin Hibbits 				if_sethwassistbits(ifp, CSUM_IP_TSO, 0);
397415516c77SSepherosa Ziehau 			else
39754db5958aSJustin Hibbits 				if_sethwassistbits(ifp, 0, CSUM_IP_TSO);
397615516c77SSepherosa Ziehau 		}
397715516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO6) {
39784db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TSO6);
39794db5958aSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TSO6)
39804db5958aSJustin Hibbits 				if_sethwassistbits(ifp, CSUM_IP6_TSO, 0);
398115516c77SSepherosa Ziehau 			else
39824db5958aSJustin Hibbits 				if_sethwassistbits(ifp, 0, CSUM_IP6_TSO);
398315516c77SSepherosa Ziehau 		}
398415516c77SSepherosa Ziehau 
398515516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
398615516c77SSepherosa Ziehau 		break;
398715516c77SSepherosa Ziehau 
398815516c77SSepherosa Ziehau 	case SIOCADDMULTI:
398915516c77SSepherosa Ziehau 	case SIOCDELMULTI:
399015516c77SSepherosa Ziehau 		HN_LOCK(sc);
399115516c77SSepherosa Ziehau 
399215516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
399315516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
399415516c77SSepherosa Ziehau 			break;
399515516c77SSepherosa Ziehau 		}
39964db5958aSJustin Hibbits 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
3997fdc4f478SSepherosa Ziehau 			/*
3998fdc4f478SSepherosa Ziehau 			 * Multicast uses mutex; use busy-wait for
3999fdc4f478SSepherosa Ziehau 			 * the RNDIS reply.
4000fdc4f478SSepherosa Ziehau 			 */
4001fdc4f478SSepherosa Ziehau 			HN_NO_SLEEPING(sc);
4002c08f7b2cSSepherosa Ziehau 			hn_rxfilter_config(sc);
4003fdc4f478SSepherosa Ziehau 			HN_SLEEPING_OK(sc);
4004fdc4f478SSepherosa Ziehau 		}
400515516c77SSepherosa Ziehau 
40069c6cae24SSepherosa Ziehau 		/* XXX vlan(4) style mcast addr maintenance */
40079c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
40089c6cae24SSepherosa Ziehau 			int old_if_flags;
40099c6cae24SSepherosa Ziehau 
40104db5958aSJustin Hibbits 			old_if_flags = if_getflags(sc->hn_vf_ifp);
40119c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
40129c6cae24SSepherosa Ziehau 
40139c6cae24SSepherosa Ziehau 			if ((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) &&
40144db5958aSJustin Hibbits 			    ((old_if_flags ^ if_getflags(sc->hn_vf_ifp)) &
40159c6cae24SSepherosa Ziehau 			     IFF_ALLMULTI))
40169c6cae24SSepherosa Ziehau 				error = hn_xpnt_vf_iocsetflags(sc);
40179c6cae24SSepherosa Ziehau 		}
40189c6cae24SSepherosa Ziehau 
401915516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
402015516c77SSepherosa Ziehau 		break;
402115516c77SSepherosa Ziehau 
402215516c77SSepherosa Ziehau 	case SIOCSIFMEDIA:
402315516c77SSepherosa Ziehau 	case SIOCGIFMEDIA:
40249c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
40259c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
40269c6cae24SSepherosa Ziehau 			/*
40279c6cae24SSepherosa Ziehau 			 * SIOCGIFMEDIA expects ifmediareq, so don't
40289c6cae24SSepherosa Ziehau 			 * create and pass ifr_vf to the VF here; just
40299c6cae24SSepherosa Ziehau 			 * replace the ifr_name.
40309c6cae24SSepherosa Ziehau 			 */
40319c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
40324db5958aSJustin Hibbits 			strlcpy(ifr->ifr_name, if_name(vf_ifp),
40339c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
40344db5958aSJustin Hibbits 			error = ifhwioctl(cmd, vf_ifp, data, curthread);
40359c6cae24SSepherosa Ziehau 			/* Restore the ifr_name. */
40364db5958aSJustin Hibbits 			strlcpy(ifr->ifr_name, if_name(ifp),
40379c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
40389c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
40399c6cae24SSepherosa Ziehau 			break;
40409c6cae24SSepherosa Ziehau 		}
40419c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
404215516c77SSepherosa Ziehau 		error = ifmedia_ioctl(ifp, ifr, &sc->hn_media, cmd);
404315516c77SSepherosa Ziehau 		break;
404415516c77SSepherosa Ziehau 
40458c068aa5SSepherosa Ziehau 	case SIOCGIFRSSHASH:
40468c068aa5SSepherosa Ziehau 		ifrh = (struct ifrsshash *)data;
40478c068aa5SSepherosa Ziehau 		HN_LOCK(sc);
40488c068aa5SSepherosa Ziehau 		if (sc->hn_rx_ring_inuse == 1) {
40498c068aa5SSepherosa Ziehau 			HN_UNLOCK(sc);
40508c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_NONE;
40518c068aa5SSepherosa Ziehau 			ifrh->ifrh_types = 0;
40528c068aa5SSepherosa Ziehau 			break;
40538c068aa5SSepherosa Ziehau 		}
40548c068aa5SSepherosa Ziehau 
40558c068aa5SSepherosa Ziehau 		if (sc->hn_rss_hash & NDIS_HASH_FUNCTION_TOEPLITZ)
40568c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_TOEPLITZ;
40578c068aa5SSepherosa Ziehau 		else
40588c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_PRIVATE;
4059642ec226SSepherosa Ziehau 		ifrh->ifrh_types = hn_rss_type_fromndis(sc->hn_rss_hash);
40608c068aa5SSepherosa Ziehau 		HN_UNLOCK(sc);
40618c068aa5SSepherosa Ziehau 		break;
40628c068aa5SSepherosa Ziehau 
40638c068aa5SSepherosa Ziehau 	case SIOCGIFRSSKEY:
40648c068aa5SSepherosa Ziehau 		ifrk = (struct ifrsskey *)data;
40658c068aa5SSepherosa Ziehau 		HN_LOCK(sc);
40668c068aa5SSepherosa Ziehau 		if (sc->hn_rx_ring_inuse == 1) {
40678c068aa5SSepherosa Ziehau 			HN_UNLOCK(sc);
40688c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_NONE;
40698c068aa5SSepherosa Ziehau 			ifrk->ifrk_keylen = 0;
40708c068aa5SSepherosa Ziehau 			break;
40718c068aa5SSepherosa Ziehau 		}
40728c068aa5SSepherosa Ziehau 		if (sc->hn_rss_hash & NDIS_HASH_FUNCTION_TOEPLITZ)
40738c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_TOEPLITZ;
40748c068aa5SSepherosa Ziehau 		else
40758c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_PRIVATE;
40768c068aa5SSepherosa Ziehau 		ifrk->ifrk_keylen = NDIS_HASH_KEYSIZE_TOEPLITZ;
40778c068aa5SSepherosa Ziehau 		memcpy(ifrk->ifrk_key, sc->hn_rss.rss_key,
40788c068aa5SSepherosa Ziehau 		    NDIS_HASH_KEYSIZE_TOEPLITZ);
40798c068aa5SSepherosa Ziehau 		HN_UNLOCK(sc);
40808c068aa5SSepherosa Ziehau 		break;
40818c068aa5SSepherosa Ziehau 
408215516c77SSepherosa Ziehau 	default:
408315516c77SSepherosa Ziehau 		error = ether_ioctl(ifp, cmd, data);
408415516c77SSepherosa Ziehau 		break;
408515516c77SSepherosa Ziehau 	}
408615516c77SSepherosa Ziehau 	return (error);
408715516c77SSepherosa Ziehau }
408815516c77SSepherosa Ziehau 
408915516c77SSepherosa Ziehau static void
40905bdfd3fdSDexuan Cui hn_stop(struct hn_softc *sc, bool detaching)
409115516c77SSepherosa Ziehau {
40924db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
409315516c77SSepherosa Ziehau 	int i;
409415516c77SSepherosa Ziehau 
409515516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
409615516c77SSepherosa Ziehau 
409715516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
409815516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
409915516c77SSepherosa Ziehau 
41009c6cae24SSepherosa Ziehau 	/* Clear RUNNING bit ASAP. */
41014db5958aSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
41029c6cae24SSepherosa Ziehau 
41036c1204dfSSepherosa Ziehau 	/* Disable polling. */
41046c1204dfSSepherosa Ziehau 	hn_polling(sc, 0);
41056c1204dfSSepherosa Ziehau 
41069c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
41079c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_ifp != NULL,
41084db5958aSJustin Hibbits 		    ("%s: VF is not attached", if_name(ifp)));
41099c6cae24SSepherosa Ziehau 
4110a97fff19SSepherosa Ziehau 		/* Mark transparent mode VF as disabled. */
4111a97fff19SSepherosa Ziehau 		hn_xpnt_vf_setdisable(sc, false /* keep hn_vf_ifp */);
41129c6cae24SSepherosa Ziehau 
41139c6cae24SSepherosa Ziehau 		/*
41149c6cae24SSepherosa Ziehau 		 * NOTE:
41159c6cae24SSepherosa Ziehau 		 * Datapath setting must happen _before_ bringing
41169c6cae24SSepherosa Ziehau 		 * the VF down.
41179c6cae24SSepherosa Ziehau 		 */
41189c6cae24SSepherosa Ziehau 		hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
41199c6cae24SSepherosa Ziehau 
41209c6cae24SSepherosa Ziehau 		/*
41219c6cae24SSepherosa Ziehau 		 * Bring the VF down.
41229c6cae24SSepherosa Ziehau 		 */
41239c6cae24SSepherosa Ziehau 		hn_xpnt_vf_saveifflags(sc);
41244db5958aSJustin Hibbits 		if_setflagbits(ifp, 0, IFF_UP);
41259c6cae24SSepherosa Ziehau 		hn_xpnt_vf_iocsetflags(sc);
41269c6cae24SSepherosa Ziehau 	}
41279c6cae24SSepherosa Ziehau 
41289c6cae24SSepherosa Ziehau 	/* Suspend data transfers. */
412915516c77SSepherosa Ziehau 	hn_suspend_data(sc);
413015516c77SSepherosa Ziehau 
413115516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
41324db5958aSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
413315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
413415516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
41355bdfd3fdSDexuan Cui 
41365bdfd3fdSDexuan Cui 	/*
41379c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is active, make sure
41389c6cae24SSepherosa Ziehau 	 * that the RX filter still allows packet reception.
41395bdfd3fdSDexuan Cui 	 */
4140962f0357SSepherosa Ziehau 	if (!detaching && (sc->hn_flags & HN_FLAG_RXVF))
41415bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
414215516c77SSepherosa Ziehau }
414315516c77SSepherosa Ziehau 
414415516c77SSepherosa Ziehau static void
414515516c77SSepherosa Ziehau hn_init_locked(struct hn_softc *sc)
414615516c77SSepherosa Ziehau {
41474db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
414815516c77SSepherosa Ziehau 	int i;
414915516c77SSepherosa Ziehau 
415015516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
415115516c77SSepherosa Ziehau 
415215516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
415315516c77SSepherosa Ziehau 		return;
415415516c77SSepherosa Ziehau 
41554db5958aSJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
415615516c77SSepherosa Ziehau 		return;
415715516c77SSepherosa Ziehau 
415815516c77SSepherosa Ziehau 	/* Configure RX filter */
4159c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
416015516c77SSepherosa Ziehau 
416115516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
41624db5958aSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
416315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
416415516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
416515516c77SSepherosa Ziehau 
416615516c77SSepherosa Ziehau 	/* Clear TX 'suspended' bit. */
416715516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_inuse);
416815516c77SSepherosa Ziehau 
41699c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
41709c6cae24SSepherosa Ziehau 		/* Initialize transparent VF. */
41719c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
41729c6cae24SSepherosa Ziehau 	}
41739c6cae24SSepherosa Ziehau 
417415516c77SSepherosa Ziehau 	/* Everything is ready; unleash! */
41754db5958aSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
41766c1204dfSSepherosa Ziehau 
41776c1204dfSSepherosa Ziehau 	/* Re-enable polling if requested. */
41786c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz > 0)
41796c1204dfSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
418015516c77SSepherosa Ziehau }
418115516c77SSepherosa Ziehau 
418215516c77SSepherosa Ziehau static void
418315516c77SSepherosa Ziehau hn_init(void *xsc)
418415516c77SSepherosa Ziehau {
418515516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
418615516c77SSepherosa Ziehau 
418715516c77SSepherosa Ziehau 	HN_LOCK(sc);
418815516c77SSepherosa Ziehau 	hn_init_locked(sc);
418915516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
419015516c77SSepherosa Ziehau }
419115516c77SSepherosa Ziehau 
419215516c77SSepherosa Ziehau static int
419315516c77SSepherosa Ziehau hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS)
419415516c77SSepherosa Ziehau {
419515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
419615516c77SSepherosa Ziehau 	unsigned int lenlim;
419715516c77SSepherosa Ziehau 	int error;
419815516c77SSepherosa Ziehau 
419915516c77SSepherosa Ziehau 	lenlim = sc->hn_rx_ring[0].hn_lro.lro_length_lim;
420015516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &lenlim, 0, req);
420115516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
420215516c77SSepherosa Ziehau 		return error;
420315516c77SSepherosa Ziehau 
420415516c77SSepherosa Ziehau 	HN_LOCK(sc);
420515516c77SSepherosa Ziehau 	if (lenlim < HN_LRO_LENLIM_MIN(sc->hn_ifp) ||
420615516c77SSepherosa Ziehau 	    lenlim > TCP_LRO_LENGTH_MAX) {
420715516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
420815516c77SSepherosa Ziehau 		return EINVAL;
420915516c77SSepherosa Ziehau 	}
421015516c77SSepherosa Ziehau 	hn_set_lro_lenlim(sc, lenlim);
421115516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
421215516c77SSepherosa Ziehau 
421315516c77SSepherosa Ziehau 	return 0;
421415516c77SSepherosa Ziehau }
421515516c77SSepherosa Ziehau 
421615516c77SSepherosa Ziehau static int
421715516c77SSepherosa Ziehau hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS)
421815516c77SSepherosa Ziehau {
421915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
422015516c77SSepherosa Ziehau 	int ackcnt, error, i;
422115516c77SSepherosa Ziehau 
422215516c77SSepherosa Ziehau 	/*
422315516c77SSepherosa Ziehau 	 * lro_ackcnt_lim is append count limit,
422415516c77SSepherosa Ziehau 	 * +1 to turn it into aggregation limit.
422515516c77SSepherosa Ziehau 	 */
422615516c77SSepherosa Ziehau 	ackcnt = sc->hn_rx_ring[0].hn_lro.lro_ackcnt_lim + 1;
422715516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &ackcnt, 0, req);
422815516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
422915516c77SSepherosa Ziehau 		return error;
423015516c77SSepherosa Ziehau 
423115516c77SSepherosa Ziehau 	if (ackcnt < 2 || ackcnt > (TCP_LRO_ACKCNT_MAX + 1))
423215516c77SSepherosa Ziehau 		return EINVAL;
423315516c77SSepherosa Ziehau 
423415516c77SSepherosa Ziehau 	/*
423515516c77SSepherosa Ziehau 	 * Convert aggregation limit back to append
423615516c77SSepherosa Ziehau 	 * count limit.
423715516c77SSepherosa Ziehau 	 */
423815516c77SSepherosa Ziehau 	--ackcnt;
423915516c77SSepherosa Ziehau 	HN_LOCK(sc);
4240a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
424115516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_ackcnt_lim = ackcnt;
424215516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
424315516c77SSepherosa Ziehau 	return 0;
424415516c77SSepherosa Ziehau }
424515516c77SSepherosa Ziehau 
424615516c77SSepherosa Ziehau static int
424715516c77SSepherosa Ziehau hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS)
424815516c77SSepherosa Ziehau {
424915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
425015516c77SSepherosa Ziehau 	int hcsum = arg2;
425115516c77SSepherosa Ziehau 	int on, error, i;
425215516c77SSepherosa Ziehau 
425315516c77SSepherosa Ziehau 	on = 0;
425415516c77SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_trust_hcsum & hcsum)
425515516c77SSepherosa Ziehau 		on = 1;
425615516c77SSepherosa Ziehau 
425715516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &on, 0, req);
425815516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
425915516c77SSepherosa Ziehau 		return error;
426015516c77SSepherosa Ziehau 
426115516c77SSepherosa Ziehau 	HN_LOCK(sc);
4262a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
426315516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
426415516c77SSepherosa Ziehau 
426515516c77SSepherosa Ziehau 		if (on)
426615516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= hcsum;
426715516c77SSepherosa Ziehau 		else
426815516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum &= ~hcsum;
426915516c77SSepherosa Ziehau 	}
427015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
427115516c77SSepherosa Ziehau 	return 0;
427215516c77SSepherosa Ziehau }
427315516c77SSepherosa Ziehau 
427415516c77SSepherosa Ziehau static int
427515516c77SSepherosa Ziehau hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS)
427615516c77SSepherosa Ziehau {
427715516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
427815516c77SSepherosa Ziehau 	int chim_size, error;
427915516c77SSepherosa Ziehau 
428015516c77SSepherosa Ziehau 	chim_size = sc->hn_tx_ring[0].hn_chim_size;
428115516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &chim_size, 0, req);
428215516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
428315516c77SSepherosa Ziehau 		return error;
428415516c77SSepherosa Ziehau 
428515516c77SSepherosa Ziehau 	if (chim_size > sc->hn_chim_szmax || chim_size <= 0)
428615516c77SSepherosa Ziehau 		return EINVAL;
428715516c77SSepherosa Ziehau 
428815516c77SSepherosa Ziehau 	HN_LOCK(sc);
428915516c77SSepherosa Ziehau 	hn_set_chim_size(sc, chim_size);
429015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
429115516c77SSepherosa Ziehau 	return 0;
429215516c77SSepherosa Ziehau }
429315516c77SSepherosa Ziehau 
429415516c77SSepherosa Ziehau static int
429515516c77SSepherosa Ziehau hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS)
429615516c77SSepherosa Ziehau {
429715516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
429815516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
429915516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
430015516c77SSepherosa Ziehau 	uint64_t stat;
430115516c77SSepherosa Ziehau 
430215516c77SSepherosa Ziehau 	stat = 0;
4303a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
430415516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
430515516c77SSepherosa Ziehau 		stat += *((uint64_t *)((uint8_t *)rxr + ofs));
430615516c77SSepherosa Ziehau 	}
430715516c77SSepherosa Ziehau 
430815516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
430915516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
431015516c77SSepherosa Ziehau 		return error;
431115516c77SSepherosa Ziehau 
431215516c77SSepherosa Ziehau 	/* Zero out this stat. */
4313a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
431415516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
431515516c77SSepherosa Ziehau 		*((uint64_t *)((uint8_t *)rxr + ofs)) = 0;
431615516c77SSepherosa Ziehau 	}
431715516c77SSepherosa Ziehau 	return 0;
431815516c77SSepherosa Ziehau }
431915516c77SSepherosa Ziehau 
432015516c77SSepherosa Ziehau static int
432115516c77SSepherosa Ziehau hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
432215516c77SSepherosa Ziehau {
432315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
432415516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
432515516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
432615516c77SSepherosa Ziehau 	u_long stat;
432715516c77SSepherosa Ziehau 
432815516c77SSepherosa Ziehau 	stat = 0;
4329a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
433015516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
433115516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)rxr + ofs));
433215516c77SSepherosa Ziehau 	}
433315516c77SSepherosa Ziehau 
433415516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
433515516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
433615516c77SSepherosa Ziehau 		return error;
433715516c77SSepherosa Ziehau 
433815516c77SSepherosa Ziehau 	/* Zero out this stat. */
4339a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
434015516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
434115516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)rxr + ofs)) = 0;
434215516c77SSepherosa Ziehau 	}
434315516c77SSepherosa Ziehau 	return 0;
434415516c77SSepherosa Ziehau }
434515516c77SSepherosa Ziehau 
434615516c77SSepherosa Ziehau static int
434715516c77SSepherosa Ziehau hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
434815516c77SSepherosa Ziehau {
434915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
435015516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
435115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
435215516c77SSepherosa Ziehau 	u_long stat;
435315516c77SSepherosa Ziehau 
435415516c77SSepherosa Ziehau 	stat = 0;
4355a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
435615516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
435715516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)txr + ofs));
435815516c77SSepherosa Ziehau 	}
435915516c77SSepherosa Ziehau 
436015516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
436115516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
436215516c77SSepherosa Ziehau 		return error;
436315516c77SSepherosa Ziehau 
436415516c77SSepherosa Ziehau 	/* Zero out this stat. */
4365a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
436615516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
436715516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)txr + ofs)) = 0;
436815516c77SSepherosa Ziehau 	}
436915516c77SSepherosa Ziehau 	return 0;
437015516c77SSepherosa Ziehau }
437115516c77SSepherosa Ziehau 
437215516c77SSepherosa Ziehau static int
437315516c77SSepherosa Ziehau hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS)
437415516c77SSepherosa Ziehau {
437515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
437615516c77SSepherosa Ziehau 	int ofs = arg2, i, error, conf;
437715516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
437815516c77SSepherosa Ziehau 
437915516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[0];
438015516c77SSepherosa Ziehau 	conf = *((int *)((uint8_t *)txr + ofs));
438115516c77SSepherosa Ziehau 
438215516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &conf, 0, req);
438315516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
438415516c77SSepherosa Ziehau 		return error;
438515516c77SSepherosa Ziehau 
438615516c77SSepherosa Ziehau 	HN_LOCK(sc);
4387a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
438815516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
438915516c77SSepherosa Ziehau 		*((int *)((uint8_t *)txr + ofs)) = conf;
439015516c77SSepherosa Ziehau 	}
439115516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
439215516c77SSepherosa Ziehau 
439315516c77SSepherosa Ziehau 	return 0;
439415516c77SSepherosa Ziehau }
439515516c77SSepherosa Ziehau 
439615516c77SSepherosa Ziehau static int
4397dc13fee6SSepherosa Ziehau hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS)
4398dc13fee6SSepherosa Ziehau {
4399dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4400dc13fee6SSepherosa Ziehau 	int error, size;
4401dc13fee6SSepherosa Ziehau 
4402dc13fee6SSepherosa Ziehau 	size = sc->hn_agg_size;
4403dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &size, 0, req);
4404dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
4405dc13fee6SSepherosa Ziehau 		return (error);
4406dc13fee6SSepherosa Ziehau 
4407dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
4408dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = size;
4409dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
4410dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
4411dc13fee6SSepherosa Ziehau 
4412dc13fee6SSepherosa Ziehau 	return (0);
4413dc13fee6SSepherosa Ziehau }
4414dc13fee6SSepherosa Ziehau 
4415dc13fee6SSepherosa Ziehau static int
4416dc13fee6SSepherosa Ziehau hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS)
4417dc13fee6SSepherosa Ziehau {
4418dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4419dc13fee6SSepherosa Ziehau 	int error, pkts;
4420dc13fee6SSepherosa Ziehau 
4421dc13fee6SSepherosa Ziehau 	pkts = sc->hn_agg_pkts;
4422dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pkts, 0, req);
4423dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
4424dc13fee6SSepherosa Ziehau 		return (error);
4425dc13fee6SSepherosa Ziehau 
4426dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
4427dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = pkts;
4428dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
4429dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
4430dc13fee6SSepherosa Ziehau 
4431dc13fee6SSepherosa Ziehau 	return (0);
4432dc13fee6SSepherosa Ziehau }
4433dc13fee6SSepherosa Ziehau 
4434dc13fee6SSepherosa Ziehau static int
4435dc13fee6SSepherosa Ziehau hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS)
4436dc13fee6SSepherosa Ziehau {
4437dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4438dc13fee6SSepherosa Ziehau 	int pkts;
4439dc13fee6SSepherosa Ziehau 
4440dc13fee6SSepherosa Ziehau 	pkts = sc->hn_tx_ring[0].hn_agg_pktmax;
4441dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &pkts, 0, req));
4442dc13fee6SSepherosa Ziehau }
4443dc13fee6SSepherosa Ziehau 
4444dc13fee6SSepherosa Ziehau static int
4445dc13fee6SSepherosa Ziehau hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS)
4446dc13fee6SSepherosa Ziehau {
4447dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4448dc13fee6SSepherosa Ziehau 	int align;
4449dc13fee6SSepherosa Ziehau 
4450dc13fee6SSepherosa Ziehau 	align = sc->hn_tx_ring[0].hn_agg_align;
4451dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &align, 0, req));
4452dc13fee6SSepherosa Ziehau }
4453dc13fee6SSepherosa Ziehau 
44546c1204dfSSepherosa Ziehau static void
44556c1204dfSSepherosa Ziehau hn_chan_polling(struct vmbus_channel *chan, u_int pollhz)
44566c1204dfSSepherosa Ziehau {
44576c1204dfSSepherosa Ziehau 	if (pollhz == 0)
44586c1204dfSSepherosa Ziehau 		vmbus_chan_poll_disable(chan);
44596c1204dfSSepherosa Ziehau 	else
44606c1204dfSSepherosa Ziehau 		vmbus_chan_poll_enable(chan, pollhz);
44616c1204dfSSepherosa Ziehau }
44626c1204dfSSepherosa Ziehau 
44636c1204dfSSepherosa Ziehau static void
44646c1204dfSSepherosa Ziehau hn_polling(struct hn_softc *sc, u_int pollhz)
44656c1204dfSSepherosa Ziehau {
44666c1204dfSSepherosa Ziehau 	int nsubch = sc->hn_rx_ring_inuse - 1;
44676c1204dfSSepherosa Ziehau 
44686c1204dfSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
44696c1204dfSSepherosa Ziehau 
44706c1204dfSSepherosa Ziehau 	if (nsubch > 0) {
44716c1204dfSSepherosa Ziehau 		struct vmbus_channel **subch;
44726c1204dfSSepherosa Ziehau 		int i;
44736c1204dfSSepherosa Ziehau 
44746c1204dfSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
44756c1204dfSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
44766c1204dfSSepherosa Ziehau 			hn_chan_polling(subch[i], pollhz);
44776c1204dfSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
44786c1204dfSSepherosa Ziehau 	}
44796c1204dfSSepherosa Ziehau 	hn_chan_polling(sc->hn_prichan, pollhz);
44806c1204dfSSepherosa Ziehau }
44816c1204dfSSepherosa Ziehau 
44826c1204dfSSepherosa Ziehau static int
44836c1204dfSSepherosa Ziehau hn_polling_sysctl(SYSCTL_HANDLER_ARGS)
44846c1204dfSSepherosa Ziehau {
44856c1204dfSSepherosa Ziehau 	struct hn_softc *sc = arg1;
44866c1204dfSSepherosa Ziehau 	int pollhz, error;
44876c1204dfSSepherosa Ziehau 
44886c1204dfSSepherosa Ziehau 	pollhz = sc->hn_pollhz;
44896c1204dfSSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pollhz, 0, req);
44906c1204dfSSepherosa Ziehau 	if (error || req->newptr == NULL)
44916c1204dfSSepherosa Ziehau 		return (error);
44926c1204dfSSepherosa Ziehau 
44936c1204dfSSepherosa Ziehau 	if (pollhz != 0 &&
44946c1204dfSSepherosa Ziehau 	    (pollhz < VMBUS_CHAN_POLLHZ_MIN || pollhz > VMBUS_CHAN_POLLHZ_MAX))
44956c1204dfSSepherosa Ziehau 		return (EINVAL);
44966c1204dfSSepherosa Ziehau 
44976c1204dfSSepherosa Ziehau 	HN_LOCK(sc);
44986c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz != pollhz) {
44996c1204dfSSepherosa Ziehau 		sc->hn_pollhz = pollhz;
45004db5958aSJustin Hibbits 		if ((if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) &&
45016c1204dfSSepherosa Ziehau 		    (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
45026c1204dfSSepherosa Ziehau 			hn_polling(sc, sc->hn_pollhz);
45036c1204dfSSepherosa Ziehau 	}
45046c1204dfSSepherosa Ziehau 	HN_UNLOCK(sc);
45056c1204dfSSepherosa Ziehau 
45066c1204dfSSepherosa Ziehau 	return (0);
45076c1204dfSSepherosa Ziehau }
45086c1204dfSSepherosa Ziehau 
4509dc13fee6SSepherosa Ziehau static int
451015516c77SSepherosa Ziehau hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS)
451115516c77SSepherosa Ziehau {
451215516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
451315516c77SSepherosa Ziehau 	char verstr[16];
451415516c77SSepherosa Ziehau 
451515516c77SSepherosa Ziehau 	snprintf(verstr, sizeof(verstr), "%u.%u",
451615516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
451715516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
451815516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
451915516c77SSepherosa Ziehau }
452015516c77SSepherosa Ziehau 
452115516c77SSepherosa Ziehau static int
452215516c77SSepherosa Ziehau hn_caps_sysctl(SYSCTL_HANDLER_ARGS)
452315516c77SSepherosa Ziehau {
452415516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
452515516c77SSepherosa Ziehau 	char caps_str[128];
452615516c77SSepherosa Ziehau 	uint32_t caps;
452715516c77SSepherosa Ziehau 
452815516c77SSepherosa Ziehau 	HN_LOCK(sc);
452915516c77SSepherosa Ziehau 	caps = sc->hn_caps;
453015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
453115516c77SSepherosa Ziehau 	snprintf(caps_str, sizeof(caps_str), "%b", caps, HN_CAP_BITS);
453215516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, caps_str, sizeof(caps_str), req);
453315516c77SSepherosa Ziehau }
453415516c77SSepherosa Ziehau 
453515516c77SSepherosa Ziehau static int
453615516c77SSepherosa Ziehau hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS)
453715516c77SSepherosa Ziehau {
453815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
453915516c77SSepherosa Ziehau 	char assist_str[128];
454015516c77SSepherosa Ziehau 	uint32_t hwassist;
454115516c77SSepherosa Ziehau 
454215516c77SSepherosa Ziehau 	HN_LOCK(sc);
45434db5958aSJustin Hibbits 	hwassist = if_gethwassist(sc->hn_ifp);
454415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
454515516c77SSepherosa Ziehau 	snprintf(assist_str, sizeof(assist_str), "%b", hwassist, CSUM_BITS);
454615516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, assist_str, sizeof(assist_str), req);
454715516c77SSepherosa Ziehau }
454815516c77SSepherosa Ziehau 
454915516c77SSepherosa Ziehau static int
455015516c77SSepherosa Ziehau hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS)
455115516c77SSepherosa Ziehau {
455215516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
455315516c77SSepherosa Ziehau 	char filter_str[128];
455415516c77SSepherosa Ziehau 	uint32_t filter;
455515516c77SSepherosa Ziehau 
455615516c77SSepherosa Ziehau 	HN_LOCK(sc);
455715516c77SSepherosa Ziehau 	filter = sc->hn_rx_filter;
455815516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
455915516c77SSepherosa Ziehau 	snprintf(filter_str, sizeof(filter_str), "%b", filter,
456015516c77SSepherosa Ziehau 	    NDIS_PACKET_TYPES);
456115516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req);
456215516c77SSepherosa Ziehau }
456315516c77SSepherosa Ziehau 
456480c3eb7bSWei Hu static int
456580c3eb7bSWei Hu hn_rsc_sysctl(SYSCTL_HANDLER_ARGS)
456680c3eb7bSWei Hu {
456780c3eb7bSWei Hu 	struct hn_softc *sc = arg1;
456880c3eb7bSWei Hu 	uint32_t mtu;
456980c3eb7bSWei Hu 	int error;
457080c3eb7bSWei Hu 	HN_LOCK(sc);
457180c3eb7bSWei Hu 	error = hn_rndis_get_mtu(sc, &mtu);
457280c3eb7bSWei Hu 	if (error) {
457380c3eb7bSWei Hu 		if_printf(sc->hn_ifp, "failed to get mtu\n");
457480c3eb7bSWei Hu 		goto back;
457580c3eb7bSWei Hu 	}
457680c3eb7bSWei Hu 	error = SYSCTL_OUT(req, &(sc->hn_rsc_ctrl), sizeof(sc->hn_rsc_ctrl));
457780c3eb7bSWei Hu 	if (error || req->newptr == NULL)
457880c3eb7bSWei Hu 		goto back;
457980c3eb7bSWei Hu 
458080c3eb7bSWei Hu 	error = SYSCTL_IN(req, &(sc->hn_rsc_ctrl), sizeof(sc->hn_rsc_ctrl));
458180c3eb7bSWei Hu 	if (error)
458280c3eb7bSWei Hu 		goto back;
458380c3eb7bSWei Hu 	error = hn_rndis_reconf_offload(sc, mtu);
458480c3eb7bSWei Hu back:
458580c3eb7bSWei Hu 	HN_UNLOCK(sc);
458680c3eb7bSWei Hu 	return (error);
458780c3eb7bSWei Hu }
458834d68912SSepherosa Ziehau #ifndef RSS
458934d68912SSepherosa Ziehau 
459015516c77SSepherosa Ziehau static int
459115516c77SSepherosa Ziehau hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS)
459215516c77SSepherosa Ziehau {
459315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
459415516c77SSepherosa Ziehau 	int error;
459515516c77SSepherosa Ziehau 
459615516c77SSepherosa Ziehau 	HN_LOCK(sc);
459715516c77SSepherosa Ziehau 
459815516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
459915516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
460015516c77SSepherosa Ziehau 		goto back;
460115516c77SSepherosa Ziehau 
4602642ec226SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) ||
4603642ec226SSepherosa Ziehau 	    (hn_xpnt_vf && sc->hn_vf_ifp != NULL)) {
4604642ec226SSepherosa Ziehau 		/*
4605642ec226SSepherosa Ziehau 		 * RSS key is synchronized w/ VF's, don't allow users
4606642ec226SSepherosa Ziehau 		 * to change it.
4607642ec226SSepherosa Ziehau 		 */
4608642ec226SSepherosa Ziehau 		error = EBUSY;
4609642ec226SSepherosa Ziehau 		goto back;
4610642ec226SSepherosa Ziehau 	}
4611642ec226SSepherosa Ziehau 
461215516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
461315516c77SSepherosa Ziehau 	if (error)
461415516c77SSepherosa Ziehau 		goto back;
461515516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
461615516c77SSepherosa Ziehau 
461715516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
461815516c77SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
461915516c77SSepherosa Ziehau 	} else {
462015516c77SSepherosa Ziehau 		/* Not RSS capable, at least for now; just save the RSS key. */
462115516c77SSepherosa Ziehau 		error = 0;
462215516c77SSepherosa Ziehau 	}
462315516c77SSepherosa Ziehau back:
462415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
462515516c77SSepherosa Ziehau 	return (error);
462615516c77SSepherosa Ziehau }
462715516c77SSepherosa Ziehau 
462815516c77SSepherosa Ziehau static int
462915516c77SSepherosa Ziehau hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS)
463015516c77SSepherosa Ziehau {
463115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
463215516c77SSepherosa Ziehau 	int error;
463315516c77SSepherosa Ziehau 
463415516c77SSepherosa Ziehau 	HN_LOCK(sc);
463515516c77SSepherosa Ziehau 
463615516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
463715516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
463815516c77SSepherosa Ziehau 		goto back;
463915516c77SSepherosa Ziehau 
464015516c77SSepherosa Ziehau 	/*
464115516c77SSepherosa Ziehau 	 * Don't allow RSS indirect table change, if this interface is not
464215516c77SSepherosa Ziehau 	 * RSS capable currently.
464315516c77SSepherosa Ziehau 	 */
464415516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
464515516c77SSepherosa Ziehau 		error = EOPNOTSUPP;
464615516c77SSepherosa Ziehau 		goto back;
464715516c77SSepherosa Ziehau 	}
464815516c77SSepherosa Ziehau 
464915516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
465015516c77SSepherosa Ziehau 	if (error)
465115516c77SSepherosa Ziehau 		goto back;
465215516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSIND;
465315516c77SSepherosa Ziehau 
4654afd4971bSSepherosa Ziehau 	hn_rss_ind_fixup(sc);
465515516c77SSepherosa Ziehau 	error = hn_rss_reconfig(sc);
465615516c77SSepherosa Ziehau back:
465715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
465815516c77SSepherosa Ziehau 	return (error);
465915516c77SSepherosa Ziehau }
466015516c77SSepherosa Ziehau 
466134d68912SSepherosa Ziehau #endif	/* !RSS */
466234d68912SSepherosa Ziehau 
466315516c77SSepherosa Ziehau static int
466415516c77SSepherosa Ziehau hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS)
466515516c77SSepherosa Ziehau {
466615516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
466715516c77SSepherosa Ziehau 	char hash_str[128];
466815516c77SSepherosa Ziehau 	uint32_t hash;
466915516c77SSepherosa Ziehau 
467015516c77SSepherosa Ziehau 	HN_LOCK(sc);
467115516c77SSepherosa Ziehau 	hash = sc->hn_rss_hash;
467215516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
467315516c77SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
467415516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
467515516c77SSepherosa Ziehau }
467615516c77SSepherosa Ziehau 
467715516c77SSepherosa Ziehau static int
4678642ec226SSepherosa Ziehau hn_rss_hcap_sysctl(SYSCTL_HANDLER_ARGS)
4679642ec226SSepherosa Ziehau {
4680642ec226SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4681642ec226SSepherosa Ziehau 	char hash_str[128];
4682642ec226SSepherosa Ziehau 	uint32_t hash;
4683642ec226SSepherosa Ziehau 
4684642ec226SSepherosa Ziehau 	HN_LOCK(sc);
4685642ec226SSepherosa Ziehau 	hash = sc->hn_rss_hcap;
4686642ec226SSepherosa Ziehau 	HN_UNLOCK(sc);
4687642ec226SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
4688642ec226SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
4689642ec226SSepherosa Ziehau }
4690642ec226SSepherosa Ziehau 
4691642ec226SSepherosa Ziehau static int
4692642ec226SSepherosa Ziehau hn_rss_mbuf_sysctl(SYSCTL_HANDLER_ARGS)
4693642ec226SSepherosa Ziehau {
4694642ec226SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4695642ec226SSepherosa Ziehau 	char hash_str[128];
4696642ec226SSepherosa Ziehau 	uint32_t hash;
4697642ec226SSepherosa Ziehau 
4698642ec226SSepherosa Ziehau 	HN_LOCK(sc);
4699642ec226SSepherosa Ziehau 	hash = sc->hn_rx_ring[0].hn_mbuf_hash;
4700642ec226SSepherosa Ziehau 	HN_UNLOCK(sc);
4701642ec226SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
4702642ec226SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
4703642ec226SSepherosa Ziehau }
4704642ec226SSepherosa Ziehau 
4705642ec226SSepherosa Ziehau static int
470640d60d6eSDexuan Cui hn_vf_sysctl(SYSCTL_HANDLER_ARGS)
470740d60d6eSDexuan Cui {
470840d60d6eSDexuan Cui 	struct hn_softc *sc = arg1;
4709499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
47104db5958aSJustin Hibbits 	if_t vf_ifp;
471140d60d6eSDexuan Cui 
471240d60d6eSDexuan Cui 	HN_LOCK(sc);
471340d60d6eSDexuan Cui 	vf_name[0] = '\0';
4714962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
4715962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
47164db5958aSJustin Hibbits 		snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf_ifp));
471740d60d6eSDexuan Cui 	HN_UNLOCK(sc);
471840d60d6eSDexuan Cui 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
471940d60d6eSDexuan Cui }
472040d60d6eSDexuan Cui 
472140d60d6eSDexuan Cui static int
4722499c3e17SSepherosa Ziehau hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS)
4723499c3e17SSepherosa Ziehau {
4724499c3e17SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4725499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
47264db5958aSJustin Hibbits 	if_t vf_ifp;
4727499c3e17SSepherosa Ziehau 
4728499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
4729499c3e17SSepherosa Ziehau 	vf_name[0] = '\0';
4730962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_rx_ring[0].hn_rxvf_ifp;
4731962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
47324db5958aSJustin Hibbits 		snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf_ifp));
4733499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
4734499c3e17SSepherosa Ziehau 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
4735499c3e17SSepherosa Ziehau }
4736499c3e17SSepherosa Ziehau 
4737499c3e17SSepherosa Ziehau static int
4738499c3e17SSepherosa Ziehau hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
4739499c3e17SSepherosa Ziehau {
4740499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4741499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4742499c3e17SSepherosa Ziehau 	int error, i;
4743499c3e17SSepherosa Ziehau 	bool first;
4744499c3e17SSepherosa Ziehau 
4745499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4746499c3e17SSepherosa Ziehau 	if (error != 0)
4747499c3e17SSepherosa Ziehau 		return (error);
4748499c3e17SSepherosa Ziehau 
4749499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4750499c3e17SSepherosa Ziehau 	if (sb == NULL)
4751499c3e17SSepherosa Ziehau 		return (ENOMEM);
4752499c3e17SSepherosa Ziehau 
4753499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4754499c3e17SSepherosa Ziehau 
4755499c3e17SSepherosa Ziehau 	first = true;
4756499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4757d74b7baeSGleb Smirnoff 		struct epoch_tracker et;
47584db5958aSJustin Hibbits 		if_t ifp;
4759499c3e17SSepherosa Ziehau 
4760499c3e17SSepherosa Ziehau 		if (hn_vfmap[i] == NULL)
4761499c3e17SSepherosa Ziehau 			continue;
4762499c3e17SSepherosa Ziehau 
4763d74b7baeSGleb Smirnoff 		NET_EPOCH_ENTER(et);
4764499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4765499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4766499c3e17SSepherosa Ziehau 			if (first)
47674db5958aSJustin Hibbits 				sbuf_printf(sb, "%s", if_name(ifp));
4768499c3e17SSepherosa Ziehau 			else
47694db5958aSJustin Hibbits 				sbuf_printf(sb, " %s", if_name(ifp));
4770499c3e17SSepherosa Ziehau 			first = false;
4771499c3e17SSepherosa Ziehau 		}
4772d74b7baeSGleb Smirnoff 		NET_EPOCH_EXIT(et);
4773499c3e17SSepherosa Ziehau 	}
4774499c3e17SSepherosa Ziehau 
4775499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4776499c3e17SSepherosa Ziehau 
4777499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4778499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4779499c3e17SSepherosa Ziehau 	return (error);
4780499c3e17SSepherosa Ziehau }
4781499c3e17SSepherosa Ziehau 
4782499c3e17SSepherosa Ziehau static int
4783499c3e17SSepherosa Ziehau hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
4784499c3e17SSepherosa Ziehau {
4785499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4786499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4787499c3e17SSepherosa Ziehau 	int error, i;
4788499c3e17SSepherosa Ziehau 	bool first;
4789499c3e17SSepherosa Ziehau 
4790499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4791499c3e17SSepherosa Ziehau 	if (error != 0)
4792499c3e17SSepherosa Ziehau 		return (error);
4793499c3e17SSepherosa Ziehau 
4794499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4795499c3e17SSepherosa Ziehau 	if (sb == NULL)
4796499c3e17SSepherosa Ziehau 		return (ENOMEM);
4797499c3e17SSepherosa Ziehau 
4798499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4799499c3e17SSepherosa Ziehau 
4800499c3e17SSepherosa Ziehau 	first = true;
4801499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4802d74b7baeSGleb Smirnoff 		struct epoch_tracker et;
48034db5958aSJustin Hibbits 		if_t ifp, hn_ifp;
4804499c3e17SSepherosa Ziehau 
4805499c3e17SSepherosa Ziehau 		hn_ifp = hn_vfmap[i];
4806499c3e17SSepherosa Ziehau 		if (hn_ifp == NULL)
4807499c3e17SSepherosa Ziehau 			continue;
4808499c3e17SSepherosa Ziehau 
4809d74b7baeSGleb Smirnoff 		NET_EPOCH_ENTER(et);
4810499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4811499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4812499c3e17SSepherosa Ziehau 			if (first) {
48134db5958aSJustin Hibbits 				sbuf_printf(sb, "%s:%s", if_name(ifp),
48144db5958aSJustin Hibbits 				    if_name(hn_ifp));
4815499c3e17SSepherosa Ziehau 			} else {
48164db5958aSJustin Hibbits 				sbuf_printf(sb, " %s:%s", if_name(ifp),
48174db5958aSJustin Hibbits 				    if_name(hn_ifp));
4818499c3e17SSepherosa Ziehau 			}
4819499c3e17SSepherosa Ziehau 			first = false;
4820499c3e17SSepherosa Ziehau 		}
4821d74b7baeSGleb Smirnoff 		NET_EPOCH_EXIT(et);
4822499c3e17SSepherosa Ziehau 	}
4823499c3e17SSepherosa Ziehau 
4824499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4825499c3e17SSepherosa Ziehau 
4826499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4827499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4828499c3e17SSepherosa Ziehau 	return (error);
4829499c3e17SSepherosa Ziehau }
4830499c3e17SSepherosa Ziehau 
4831499c3e17SSepherosa Ziehau static int
48329c6cae24SSepherosa Ziehau hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS)
48339c6cae24SSepherosa Ziehau {
48349c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
48359c6cae24SSepherosa Ziehau 	int error, onoff = 0;
48369c6cae24SSepherosa Ziehau 
48379c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF)
48389c6cae24SSepherosa Ziehau 		onoff = 1;
48399c6cae24SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &onoff, 0, req);
48409c6cae24SSepherosa Ziehau 	if (error || req->newptr == NULL)
48419c6cae24SSepherosa Ziehau 		return (error);
48429c6cae24SSepherosa Ziehau 
48439c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
48449c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit() */
48459c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
48469c6cae24SSepherosa Ziehau 	if (onoff)
48479c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
48489c6cae24SSepherosa Ziehau 	else
48499c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags &= ~HN_XVFFLAG_ACCBPF;
48509c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
48519c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
48529c6cae24SSepherosa Ziehau 
48539c6cae24SSepherosa Ziehau 	return (0);
48549c6cae24SSepherosa Ziehau }
48559c6cae24SSepherosa Ziehau 
48569c6cae24SSepherosa Ziehau static int
48579c6cae24SSepherosa Ziehau hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS)
48589c6cae24SSepherosa Ziehau {
48599c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
48609c6cae24SSepherosa Ziehau 	int enabled = 0;
48619c6cae24SSepherosa Ziehau 
48629c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
48639c6cae24SSepherosa Ziehau 		enabled = 1;
48649c6cae24SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &enabled, 0, req));
48659c6cae24SSepherosa Ziehau }
48669c6cae24SSepherosa Ziehau 
48679c6cae24SSepherosa Ziehau static int
486815516c77SSepherosa Ziehau hn_check_iplen(const struct mbuf *m, int hoff)
486915516c77SSepherosa Ziehau {
487015516c77SSepherosa Ziehau 	const struct ip *ip;
487115516c77SSepherosa Ziehau 	int len, iphlen, iplen;
487215516c77SSepherosa Ziehau 	const struct tcphdr *th;
487315516c77SSepherosa Ziehau 	int thoff;				/* TCP data offset */
487415516c77SSepherosa Ziehau 
487515516c77SSepherosa Ziehau 	len = hoff + sizeof(struct ip);
487615516c77SSepherosa Ziehau 
487715516c77SSepherosa Ziehau 	/* The packet must be at least the size of an IP header. */
487815516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < len)
487915516c77SSepherosa Ziehau 		return IPPROTO_DONE;
488015516c77SSepherosa Ziehau 
488115516c77SSepherosa Ziehau 	/* The fixed IP header must reside completely in the first mbuf. */
488215516c77SSepherosa Ziehau 	if (m->m_len < len)
488315516c77SSepherosa Ziehau 		return IPPROTO_DONE;
488415516c77SSepherosa Ziehau 
488515516c77SSepherosa Ziehau 	ip = mtodo(m, hoff);
488615516c77SSepherosa Ziehau 
488715516c77SSepherosa Ziehau 	/* Bound check the packet's stated IP header length. */
488815516c77SSepherosa Ziehau 	iphlen = ip->ip_hl << 2;
488915516c77SSepherosa Ziehau 	if (iphlen < sizeof(struct ip))		/* minimum header length */
489015516c77SSepherosa Ziehau 		return IPPROTO_DONE;
489115516c77SSepherosa Ziehau 
489215516c77SSepherosa Ziehau 	/* The full IP header must reside completely in the one mbuf. */
489315516c77SSepherosa Ziehau 	if (m->m_len < hoff + iphlen)
489415516c77SSepherosa Ziehau 		return IPPROTO_DONE;
489515516c77SSepherosa Ziehau 
489615516c77SSepherosa Ziehau 	iplen = ntohs(ip->ip_len);
489715516c77SSepherosa Ziehau 
489815516c77SSepherosa Ziehau 	/*
489915516c77SSepherosa Ziehau 	 * Check that the amount of data in the buffers is as
490015516c77SSepherosa Ziehau 	 * at least much as the IP header would have us expect.
490115516c77SSepherosa Ziehau 	 */
490215516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < hoff + iplen)
490315516c77SSepherosa Ziehau 		return IPPROTO_DONE;
490415516c77SSepherosa Ziehau 
490515516c77SSepherosa Ziehau 	/*
490615516c77SSepherosa Ziehau 	 * Ignore IP fragments.
490715516c77SSepherosa Ziehau 	 */
490815516c77SSepherosa Ziehau 	if (ntohs(ip->ip_off) & (IP_OFFMASK | IP_MF))
490915516c77SSepherosa Ziehau 		return IPPROTO_DONE;
491015516c77SSepherosa Ziehau 
491115516c77SSepherosa Ziehau 	/*
491215516c77SSepherosa Ziehau 	 * The TCP/IP or UDP/IP header must be entirely contained within
491315516c77SSepherosa Ziehau 	 * the first fragment of a packet.
491415516c77SSepherosa Ziehau 	 */
491515516c77SSepherosa Ziehau 	switch (ip->ip_p) {
491615516c77SSepherosa Ziehau 	case IPPROTO_TCP:
491715516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct tcphdr))
491815516c77SSepherosa Ziehau 			return IPPROTO_DONE;
491915516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct tcphdr))
492015516c77SSepherosa Ziehau 			return IPPROTO_DONE;
492115516c77SSepherosa Ziehau 		th = (const struct tcphdr *)((const uint8_t *)ip + iphlen);
492215516c77SSepherosa Ziehau 		thoff = th->th_off << 2;
492315516c77SSepherosa Ziehau 		if (thoff < sizeof(struct tcphdr) || thoff + iphlen > iplen)
492415516c77SSepherosa Ziehau 			return IPPROTO_DONE;
492515516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + thoff)
492615516c77SSepherosa Ziehau 			return IPPROTO_DONE;
492715516c77SSepherosa Ziehau 		break;
492815516c77SSepherosa Ziehau 	case IPPROTO_UDP:
492915516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct udphdr))
493015516c77SSepherosa Ziehau 			return IPPROTO_DONE;
493115516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct udphdr))
493215516c77SSepherosa Ziehau 			return IPPROTO_DONE;
493315516c77SSepherosa Ziehau 		break;
493415516c77SSepherosa Ziehau 	default:
493515516c77SSepherosa Ziehau 		if (iplen < iphlen)
493615516c77SSepherosa Ziehau 			return IPPROTO_DONE;
493715516c77SSepherosa Ziehau 		break;
493815516c77SSepherosa Ziehau 	}
493915516c77SSepherosa Ziehau 	return ip->ip_p;
494015516c77SSepherosa Ziehau }
494115516c77SSepherosa Ziehau 
4942db76829bSSepherosa Ziehau static void
4943db76829bSSepherosa Ziehau hn_rxpkt_proto(const struct mbuf *m_new, int *l3proto, int *l4proto)
4944db76829bSSepherosa Ziehau {
4945db76829bSSepherosa Ziehau 	const struct ether_header *eh;
4946db76829bSSepherosa Ziehau 	uint16_t etype;
4947db76829bSSepherosa Ziehau 	int hoff;
4948db76829bSSepherosa Ziehau 
4949db76829bSSepherosa Ziehau 	hoff = sizeof(*eh);
4950db76829bSSepherosa Ziehau 	/* Checked at the beginning of this function. */
4951db76829bSSepherosa Ziehau 	KASSERT(m_new->m_len >= hoff, ("not ethernet frame"));
4952db76829bSSepherosa Ziehau 
4953db76829bSSepherosa Ziehau 	eh = mtod(m_new, const struct ether_header *);
4954db76829bSSepherosa Ziehau 	etype = ntohs(eh->ether_type);
4955db76829bSSepherosa Ziehau 	if (etype == ETHERTYPE_VLAN) {
4956db76829bSSepherosa Ziehau 		const struct ether_vlan_header *evl;
4957db76829bSSepherosa Ziehau 
4958db76829bSSepherosa Ziehau 		hoff = sizeof(*evl);
4959db76829bSSepherosa Ziehau 		if (m_new->m_len < hoff)
4960db76829bSSepherosa Ziehau 			return;
4961db76829bSSepherosa Ziehau 		evl = mtod(m_new, const struct ether_vlan_header *);
4962db76829bSSepherosa Ziehau 		etype = ntohs(evl->evl_proto);
4963db76829bSSepherosa Ziehau 	}
4964db76829bSSepherosa Ziehau 	*l3proto = etype;
4965db76829bSSepherosa Ziehau 
4966db76829bSSepherosa Ziehau 	if (etype == ETHERTYPE_IP)
4967db76829bSSepherosa Ziehau 		*l4proto = hn_check_iplen(m_new, hoff);
4968db76829bSSepherosa Ziehau 	else
4969db76829bSSepherosa Ziehau 		*l4proto = IPPROTO_DONE;
4970db76829bSSepherosa Ziehau }
4971db76829bSSepherosa Ziehau 
497215516c77SSepherosa Ziehau static int
497315516c77SSepherosa Ziehau hn_create_rx_data(struct hn_softc *sc, int ring_cnt)
497415516c77SSepherosa Ziehau {
497515516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
497615516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
497715516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
497815516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
497915516c77SSepherosa Ziehau 	int lroent_cnt;
498015516c77SSepherosa Ziehau #endif
498115516c77SSepherosa Ziehau 	int i;
498215516c77SSepherosa Ziehau 
498315516c77SSepherosa Ziehau 	/*
498415516c77SSepherosa Ziehau 	 * Create RXBUF for reception.
498515516c77SSepherosa Ziehau 	 *
498615516c77SSepherosa Ziehau 	 * NOTE:
498715516c77SSepherosa Ziehau 	 * - It is shared by all channels.
498815516c77SSepherosa Ziehau 	 * - A large enough buffer is allocated, certain version of NVSes
498915516c77SSepherosa Ziehau 	 *   may further limit the usable space.
499015516c77SSepherosa Ziehau 	 */
4991*62f9bcf2SAndrew Turner 	sc->hn_rxbuf = contigmalloc(HN_RXBUF_SIZE, M_DEVBUF, M_WAITOK | M_ZERO,
4992*62f9bcf2SAndrew Turner 	    0ul, ~0ul, PAGE_SIZE, 0);
499315516c77SSepherosa Ziehau 	if (sc->hn_rxbuf == NULL) {
499415516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate rxbuf failed\n");
499515516c77SSepherosa Ziehau 		return (ENOMEM);
499615516c77SSepherosa Ziehau 	}
499715516c77SSepherosa Ziehau 
499815516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = ring_cnt;
499915516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = sc->hn_rx_ring_cnt;
500015516c77SSepherosa Ziehau 
500115516c77SSepherosa Ziehau 	sc->hn_rx_ring = malloc(sizeof(struct hn_rx_ring) * sc->hn_rx_ring_cnt,
500215516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
500315516c77SSepherosa Ziehau 
500415516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
500515516c77SSepherosa Ziehau 	lroent_cnt = hn_lro_entry_count;
500615516c77SSepherosa Ziehau 	if (lroent_cnt < TCP_LRO_ENTRIES)
500715516c77SSepherosa Ziehau 		lroent_cnt = TCP_LRO_ENTRIES;
500815516c77SSepherosa Ziehau 	if (bootverbose)
500915516c77SSepherosa Ziehau 		device_printf(dev, "LRO: entry count %d\n", lroent_cnt);
501015516c77SSepherosa Ziehau #endif	/* INET || INET6 */
501115516c77SSepherosa Ziehau 
501215516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
501315516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
501415516c77SSepherosa Ziehau 
501515516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.rx sysctl tree */
501615516c77SSepherosa Ziehau 	sc->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rx",
501715516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
501815516c77SSepherosa Ziehau 
501915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
502015516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
502115516c77SSepherosa Ziehau 
5022*62f9bcf2SAndrew Turner 		rxr->hn_br = contigmalloc(HN_TXBR_SIZE + HN_RXBR_SIZE, M_DEVBUF,
5023*62f9bcf2SAndrew Turner 		    M_WAITOK | M_ZERO, 0ul, ~0ul, PAGE_SIZE, 0);
502415516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL) {
502515516c77SSepherosa Ziehau 			device_printf(dev, "allocate bufring failed\n");
502615516c77SSepherosa Ziehau 			return (ENOMEM);
502715516c77SSepherosa Ziehau 		}
502815516c77SSepherosa Ziehau 
502915516c77SSepherosa Ziehau 		if (hn_trust_hosttcp)
503015516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_TCP;
503115516c77SSepherosa Ziehau 		if (hn_trust_hostudp)
503215516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_UDP;
503315516c77SSepherosa Ziehau 		if (hn_trust_hostip)
503415516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_IP;
5035642ec226SSepherosa Ziehau 		rxr->hn_mbuf_hash = NDIS_HASH_ALL;
503615516c77SSepherosa Ziehau 		rxr->hn_ifp = sc->hn_ifp;
503715516c77SSepherosa Ziehau 		if (i < sc->hn_tx_ring_cnt)
503815516c77SSepherosa Ziehau 			rxr->hn_txr = &sc->hn_tx_ring[i];
503915516c77SSepherosa Ziehau 		rxr->hn_pktbuf_len = HN_PKTBUF_LEN_DEF;
504015516c77SSepherosa Ziehau 		rxr->hn_pktbuf = malloc(rxr->hn_pktbuf_len, M_DEVBUF, M_WAITOK);
504115516c77SSepherosa Ziehau 		rxr->hn_rx_idx = i;
504215516c77SSepherosa Ziehau 		rxr->hn_rxbuf = sc->hn_rxbuf;
504315516c77SSepherosa Ziehau 
504415516c77SSepherosa Ziehau 		/*
504515516c77SSepherosa Ziehau 		 * Initialize LRO.
504615516c77SSepherosa Ziehau 		 */
504715516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
504815516c77SSepherosa Ziehau 		tcp_lro_init_args(&rxr->hn_lro, sc->hn_ifp, lroent_cnt,
504915516c77SSepherosa Ziehau 		    hn_lro_mbufq_depth);
505015516c77SSepherosa Ziehau 		rxr->hn_lro.lro_length_lim = HN_LRO_LENLIM_DEF;
505115516c77SSepherosa Ziehau 		rxr->hn_lro.lro_ackcnt_lim = HN_LRO_ACKCNT_DEF;
505215516c77SSepherosa Ziehau #endif	/* INET || INET6 */
505315516c77SSepherosa Ziehau 
505415516c77SSepherosa Ziehau 		if (sc->hn_rx_sysctl_tree != NULL) {
505515516c77SSepherosa Ziehau 			char name[16];
505615516c77SSepherosa Ziehau 
505715516c77SSepherosa Ziehau 			/*
505815516c77SSepherosa Ziehau 			 * Create per RX ring sysctl tree:
505915516c77SSepherosa Ziehau 			 * dev.hn.UNIT.rx.RINGID
506015516c77SSepherosa Ziehau 			 */
506115516c77SSepherosa Ziehau 			snprintf(name, sizeof(name), "%d", i);
506215516c77SSepherosa Ziehau 			rxr->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx,
506315516c77SSepherosa Ziehau 			    SYSCTL_CHILDREN(sc->hn_rx_sysctl_tree),
506415516c77SSepherosa Ziehau 			    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
506515516c77SSepherosa Ziehau 
506615516c77SSepherosa Ziehau 			if (rxr->hn_rx_sysctl_tree != NULL) {
506715516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
506815516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5069db0ac6deSCy Schubert 				    OID_AUTO, "packets",
5070db0ac6deSCy Schubert 				    CTLFLAG_RW | CTLFLAG_STATS, &rxr->hn_pkts,
5071db0ac6deSCy Schubert 				    "# of packets received");
507215516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
507315516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5074db0ac6deSCy Schubert 				    OID_AUTO, "rss_pkts",
5075db0ac6deSCy Schubert 				    CTLFLAG_RW | CTLFLAG_STATS,
507615516c77SSepherosa Ziehau 				    &rxr->hn_rss_pkts,
507715516c77SSepherosa Ziehau 				    "# of packets w/ RSS info received");
5078a491581fSWei Hu 				SYSCTL_ADD_ULONG(ctx,
5079a491581fSWei Hu 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5080db0ac6deSCy Schubert 				    OID_AUTO, "rsc_pkts",
5081db0ac6deSCy Schubert 				    CTLFLAG_RW | CTLFLAG_STATS,
5082a491581fSWei Hu 				    &rxr->hn_rsc_pkts,
5083a491581fSWei Hu 				    "# of RSC packets received");
5084a491581fSWei Hu 				SYSCTL_ADD_ULONG(ctx,
5085a491581fSWei Hu 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5086db0ac6deSCy Schubert 				    OID_AUTO, "rsc_drop",
5087db0ac6deSCy Schubert 				    CTLFLAG_RW | CTLFLAG_STATS,
5088a491581fSWei Hu 				    &rxr->hn_rsc_drop,
5089a491581fSWei Hu 				    "# of RSC fragments dropped");
509015516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx,
509115516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
509215516c77SSepherosa Ziehau 				    OID_AUTO, "pktbuf_len", CTLFLAG_RD,
509315516c77SSepherosa Ziehau 				    &rxr->hn_pktbuf_len, 0,
509415516c77SSepherosa Ziehau 				    "Temporary channel packet buffer length");
509515516c77SSepherosa Ziehau 			}
509615516c77SSepherosa Ziehau 		}
509715516c77SSepherosa Ziehau 	}
509815516c77SSepherosa Ziehau 
509915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_queued",
5100db0ac6deSCy Schubert 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
510115516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_queued),
510215516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
510315516c77SSepherosa Ziehau 	    "LU", "LRO queued");
510415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_flushed",
5105db0ac6deSCy Schubert 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
510615516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_flushed),
510715516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
510815516c77SSepherosa Ziehau 	    "LU", "LRO flushed");
510915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_tried",
5110db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
511115516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro_tried),
511215516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of LRO tries");
511315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_length_lim",
511415516c77SSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
511515516c77SSepherosa Ziehau 	    hn_lro_lenlim_sysctl, "IU",
511615516c77SSepherosa Ziehau 	    "Max # of data bytes to be aggregated by LRO");
511715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_ackcnt_lim",
511815516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
511915516c77SSepherosa Ziehau 	    hn_lro_ackcnt_sysctl, "I",
512015516c77SSepherosa Ziehau 	    "Max # of ACKs to be aggregated by LRO");
512115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hosttcp",
512215516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_TCP,
512315516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
5124b15a632cSGordon Bergling 	    "Trust tcp segment verification on host side, "
512515516c77SSepherosa Ziehau 	    "when csum info is missing");
512615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostudp",
512715516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_UDP,
512815516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
512915516c77SSepherosa Ziehau 	    "Trust udp datagram verification on host side, "
513015516c77SSepherosa Ziehau 	    "when csum info is missing");
513115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostip",
513215516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_IP,
513315516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
513415516c77SSepherosa Ziehau 	    "Trust ip packet verification on host side, "
513515516c77SSepherosa Ziehau 	    "when csum info is missing");
513615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_ip",
5137db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
513815516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_ip),
513915516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM IP");
514015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_tcp",
5141db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
514215516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_tcp),
514315516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM TCP");
514415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_udp",
5145db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
514615516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_udp),
514715516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM UDP");
514815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_trusted",
514915516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
515015516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_trusted),
515115516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU",
515215516c77SSepherosa Ziehau 	    "# of packets that we trust host's csum verification");
515315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "small_pkts",
5154db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
515515516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_small_pkts),
515615516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of small packets received");
515715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_ack_failed",
5158db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
515915516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_ack_failed),
516015516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of RXBUF ack failures");
516115516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_cnt",
516215516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_cnt, 0, "# created RX rings");
516315516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_inuse",
516415516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_inuse, 0, "# used RX rings");
516515516c77SSepherosa Ziehau 
516615516c77SSepherosa Ziehau 	return (0);
516715516c77SSepherosa Ziehau }
516815516c77SSepherosa Ziehau 
516915516c77SSepherosa Ziehau static void
517015516c77SSepherosa Ziehau hn_destroy_rx_data(struct hn_softc *sc)
517115516c77SSepherosa Ziehau {
517215516c77SSepherosa Ziehau 	int i;
517315516c77SSepherosa Ziehau 
517415516c77SSepherosa Ziehau 	if (sc->hn_rxbuf != NULL) {
51752494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXBUF_REF) == 0)
5176*62f9bcf2SAndrew Turner 			contigfree(sc->hn_rxbuf, HN_RXBUF_SIZE, M_DEVBUF);
51772494d735SSepherosa Ziehau 		else
51782494d735SSepherosa Ziehau 			device_printf(sc->hn_dev, "RXBUF is referenced\n");
517915516c77SSepherosa Ziehau 		sc->hn_rxbuf = NULL;
518015516c77SSepherosa Ziehau 	}
518115516c77SSepherosa Ziehau 
518215516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_cnt == 0)
518315516c77SSepherosa Ziehau 		return;
518415516c77SSepherosa Ziehau 
518515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
518615516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
518715516c77SSepherosa Ziehau 
518815516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL)
518915516c77SSepherosa Ziehau 			continue;
51902494d735SSepherosa Ziehau 		if ((rxr->hn_rx_flags & HN_RX_FLAG_BR_REF) == 0) {
5191*62f9bcf2SAndrew Turner 			contigfree(rxr->hn_br, HN_TXBR_SIZE + HN_RXBR_SIZE,
5192*62f9bcf2SAndrew Turner 			    M_DEVBUF);
51932494d735SSepherosa Ziehau 		} else {
51942494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
51952494d735SSepherosa Ziehau 			    "%dth channel bufring is referenced", i);
51962494d735SSepherosa Ziehau 		}
519715516c77SSepherosa Ziehau 		rxr->hn_br = NULL;
519815516c77SSepherosa Ziehau 
519915516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
520015516c77SSepherosa Ziehau 		tcp_lro_free(&rxr->hn_lro);
520115516c77SSepherosa Ziehau #endif
520215516c77SSepherosa Ziehau 		free(rxr->hn_pktbuf, M_DEVBUF);
520315516c77SSepherosa Ziehau 	}
520415516c77SSepherosa Ziehau 	free(sc->hn_rx_ring, M_DEVBUF);
520515516c77SSepherosa Ziehau 	sc->hn_rx_ring = NULL;
520615516c77SSepherosa Ziehau 
520715516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = 0;
520815516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = 0;
520915516c77SSepherosa Ziehau }
521015516c77SSepherosa Ziehau 
521115516c77SSepherosa Ziehau static int
521215516c77SSepherosa Ziehau hn_tx_ring_create(struct hn_softc *sc, int id)
521315516c77SSepherosa Ziehau {
521415516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[id];
521515516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
521615516c77SSepherosa Ziehau 	bus_dma_tag_t parent_dtag;
521715516c77SSepherosa Ziehau 	int error, i;
521815516c77SSepherosa Ziehau 
521915516c77SSepherosa Ziehau 	txr->hn_sc = sc;
522015516c77SSepherosa Ziehau 	txr->hn_tx_idx = id;
522115516c77SSepherosa Ziehau 
522215516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
522315516c77SSepherosa Ziehau 	mtx_init(&txr->hn_txlist_spin, "hn txlist", NULL, MTX_SPIN);
522415516c77SSepherosa Ziehau #endif
522515516c77SSepherosa Ziehau 	mtx_init(&txr->hn_tx_lock, "hn tx", NULL, MTX_DEF);
522615516c77SSepherosa Ziehau 
522715516c77SSepherosa Ziehau 	txr->hn_txdesc_cnt = HN_TX_DESC_CNT;
522815516c77SSepherosa Ziehau 	txr->hn_txdesc = malloc(sizeof(struct hn_txdesc) * txr->hn_txdesc_cnt,
522915516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
523015516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
523115516c77SSepherosa Ziehau 	SLIST_INIT(&txr->hn_txlist);
523215516c77SSepherosa Ziehau #else
523315516c77SSepherosa Ziehau 	txr->hn_txdesc_br = buf_ring_alloc(txr->hn_txdesc_cnt, M_DEVBUF,
523415516c77SSepherosa Ziehau 	    M_WAITOK, &txr->hn_tx_lock);
523515516c77SSepherosa Ziehau #endif
523615516c77SSepherosa Ziehau 
52370e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_EVTTQ) {
52380e11868dSSepherosa Ziehau 		txr->hn_tx_taskq = VMBUS_GET_EVENT_TASKQ(
52390e11868dSSepherosa Ziehau 		    device_get_parent(dev), dev, HN_RING_IDX2CPU(sc, id));
52400e11868dSSepherosa Ziehau 	} else {
5241fdd0222aSSepherosa Ziehau 		txr->hn_tx_taskq = sc->hn_tx_taskqs[id % hn_tx_taskq_cnt];
52420e11868dSSepherosa Ziehau 	}
524315516c77SSepherosa Ziehau 
524423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
524515516c77SSepherosa Ziehau 	if (hn_use_if_start) {
524615516c77SSepherosa Ziehau 		txr->hn_txeof = hn_start_txeof;
524715516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_start_taskfunc, txr);
524815516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_start_txeof_taskfunc, txr);
524923bf9e15SSepherosa Ziehau 	} else
525023bf9e15SSepherosa Ziehau #endif
525123bf9e15SSepherosa Ziehau 	{
525215516c77SSepherosa Ziehau 		int br_depth;
525315516c77SSepherosa Ziehau 
525415516c77SSepherosa Ziehau 		txr->hn_txeof = hn_xmit_txeof;
525515516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_xmit_taskfunc, txr);
525615516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_xmit_txeof_taskfunc, txr);
525715516c77SSepherosa Ziehau 
525815516c77SSepherosa Ziehau 		br_depth = hn_get_txswq_depth(txr);
525915516c77SSepherosa Ziehau 		txr->hn_mbuf_br = buf_ring_alloc(br_depth, M_DEVBUF,
526015516c77SSepherosa Ziehau 		    M_WAITOK, &txr->hn_tx_lock);
526115516c77SSepherosa Ziehau 	}
526215516c77SSepherosa Ziehau 
526315516c77SSepherosa Ziehau 	txr->hn_direct_tx_size = hn_direct_tx_size;
526415516c77SSepherosa Ziehau 
526515516c77SSepherosa Ziehau 	/*
526615516c77SSepherosa Ziehau 	 * Always schedule transmission instead of trying to do direct
526715516c77SSepherosa Ziehau 	 * transmission.  This one gives the best performance so far.
526815516c77SSepherosa Ziehau 	 */
526915516c77SSepherosa Ziehau 	txr->hn_sched_tx = 1;
527015516c77SSepherosa Ziehau 
527115516c77SSepherosa Ziehau 	parent_dtag = bus_get_dma_tag(dev);
527215516c77SSepherosa Ziehau 
527315516c77SSepherosa Ziehau 	/* DMA tag for RNDIS packet messages. */
527415516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
527515516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_ALIGN,		/* alignment */
527615516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_BOUNDARY,	/* boundary */
527715516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
527815516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
527915516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
528015516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsize */
528115516c77SSepherosa Ziehau 	    1,				/* nsegments */
528215516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsegsize */
528315516c77SSepherosa Ziehau 	    0,				/* flags */
528415516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
528515516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
528615516c77SSepherosa Ziehau 	    &txr->hn_tx_rndis_dtag);
528715516c77SSepherosa Ziehau 	if (error) {
528815516c77SSepherosa Ziehau 		device_printf(dev, "failed to create rndis dmatag\n");
528915516c77SSepherosa Ziehau 		return error;
529015516c77SSepherosa Ziehau 	}
529115516c77SSepherosa Ziehau 
529215516c77SSepherosa Ziehau 	/* DMA tag for data. */
529315516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
529415516c77SSepherosa Ziehau 	    1,				/* alignment */
529515516c77SSepherosa Ziehau 	    HN_TX_DATA_BOUNDARY,	/* boundary */
529615516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
529715516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
529815516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
529915516c77SSepherosa Ziehau 	    HN_TX_DATA_MAXSIZE,		/* maxsize */
530015516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGCNT_MAX,	/* nsegments */
530115516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGSIZE,		/* maxsegsize */
530215516c77SSepherosa Ziehau 	    0,				/* flags */
530315516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
530415516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
530515516c77SSepherosa Ziehau 	    &txr->hn_tx_data_dtag);
530615516c77SSepherosa Ziehau 	if (error) {
530715516c77SSepherosa Ziehau 		device_printf(dev, "failed to create data dmatag\n");
530815516c77SSepherosa Ziehau 		return error;
530915516c77SSepherosa Ziehau 	}
531015516c77SSepherosa Ziehau 
531115516c77SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i) {
531215516c77SSepherosa Ziehau 		struct hn_txdesc *txd = &txr->hn_txdesc[i];
531315516c77SSepherosa Ziehau 
531415516c77SSepherosa Ziehau 		txd->txr = txr;
531515516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
5316dc13fee6SSepherosa Ziehau 		STAILQ_INIT(&txd->agg_list);
531715516c77SSepherosa Ziehau 
531815516c77SSepherosa Ziehau 		/*
531915516c77SSepherosa Ziehau 		 * Allocate and load RNDIS packet message.
532015516c77SSepherosa Ziehau 		 */
532115516c77SSepherosa Ziehau         	error = bus_dmamem_alloc(txr->hn_tx_rndis_dtag,
532215516c77SSepherosa Ziehau 		    (void **)&txd->rndis_pkt,
532315516c77SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
532415516c77SSepherosa Ziehau 		    &txd->rndis_pkt_dmap);
532515516c77SSepherosa Ziehau 		if (error) {
532615516c77SSepherosa Ziehau 			device_printf(dev,
532715516c77SSepherosa Ziehau 			    "failed to allocate rndis_packet_msg, %d\n", i);
532815516c77SSepherosa Ziehau 			return error;
532915516c77SSepherosa Ziehau 		}
533015516c77SSepherosa Ziehau 
533115516c77SSepherosa Ziehau 		error = bus_dmamap_load(txr->hn_tx_rndis_dtag,
533215516c77SSepherosa Ziehau 		    txd->rndis_pkt_dmap,
533315516c77SSepherosa Ziehau 		    txd->rndis_pkt, HN_RNDIS_PKT_LEN,
533415516c77SSepherosa Ziehau 		    hyperv_dma_map_paddr, &txd->rndis_pkt_paddr,
533515516c77SSepherosa Ziehau 		    BUS_DMA_NOWAIT);
533615516c77SSepherosa Ziehau 		if (error) {
533715516c77SSepherosa Ziehau 			device_printf(dev,
533815516c77SSepherosa Ziehau 			    "failed to load rndis_packet_msg, %d\n", i);
533915516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
534015516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
534115516c77SSepherosa Ziehau 			return error;
534215516c77SSepherosa Ziehau 		}
534315516c77SSepherosa Ziehau 
534415516c77SSepherosa Ziehau 		/* DMA map for TX data. */
534515516c77SSepherosa Ziehau 		error = bus_dmamap_create(txr->hn_tx_data_dtag, 0,
534615516c77SSepherosa Ziehau 		    &txd->data_dmap);
534715516c77SSepherosa Ziehau 		if (error) {
534815516c77SSepherosa Ziehau 			device_printf(dev,
534915516c77SSepherosa Ziehau 			    "failed to allocate tx data dmamap\n");
535015516c77SSepherosa Ziehau 			bus_dmamap_unload(txr->hn_tx_rndis_dtag,
535115516c77SSepherosa Ziehau 			    txd->rndis_pkt_dmap);
535215516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
535315516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
535415516c77SSepherosa Ziehau 			return error;
535515516c77SSepherosa Ziehau 		}
535615516c77SSepherosa Ziehau 
535715516c77SSepherosa Ziehau 		/* All set, put it to list */
535815516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_ONLIST;
535915516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
536015516c77SSepherosa Ziehau 		SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
536115516c77SSepherosa Ziehau #else
536215516c77SSepherosa Ziehau 		buf_ring_enqueue(txr->hn_txdesc_br, txd);
536315516c77SSepherosa Ziehau #endif
536415516c77SSepherosa Ziehau 	}
536515516c77SSepherosa Ziehau 	txr->hn_txdesc_avail = txr->hn_txdesc_cnt;
536615516c77SSepherosa Ziehau 
536715516c77SSepherosa Ziehau 	if (sc->hn_tx_sysctl_tree != NULL) {
536815516c77SSepherosa Ziehau 		struct sysctl_oid_list *child;
536915516c77SSepherosa Ziehau 		struct sysctl_ctx_list *ctx;
537015516c77SSepherosa Ziehau 		char name[16];
537115516c77SSepherosa Ziehau 
537215516c77SSepherosa Ziehau 		/*
537315516c77SSepherosa Ziehau 		 * Create per TX ring sysctl tree:
537415516c77SSepherosa Ziehau 		 * dev.hn.UNIT.tx.RINGID
537515516c77SSepherosa Ziehau 		 */
537615516c77SSepherosa Ziehau 		ctx = device_get_sysctl_ctx(dev);
537715516c77SSepherosa Ziehau 		child = SYSCTL_CHILDREN(sc->hn_tx_sysctl_tree);
537815516c77SSepherosa Ziehau 
537915516c77SSepherosa Ziehau 		snprintf(name, sizeof(name), "%d", id);
538015516c77SSepherosa Ziehau 		txr->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
538115516c77SSepherosa Ziehau 		    name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
538215516c77SSepherosa Ziehau 
538315516c77SSepherosa Ziehau 		if (txr->hn_tx_sysctl_tree != NULL) {
538415516c77SSepherosa Ziehau 			child = SYSCTL_CHILDREN(txr->hn_tx_sysctl_tree);
538515516c77SSepherosa Ziehau 
538685e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
538715516c77SSepherosa Ziehau 			SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_avail",
538815516c77SSepherosa Ziehau 			    CTLFLAG_RD, &txr->hn_txdesc_avail, 0,
538915516c77SSepherosa Ziehau 			    "# of available TX descs");
539085e4ae1eSSepherosa Ziehau #endif
539123bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
539223bf9e15SSepherosa Ziehau 			if (!hn_use_if_start)
539323bf9e15SSepherosa Ziehau #endif
539423bf9e15SSepherosa Ziehau 			{
539515516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx, child, OID_AUTO, "oactive",
539615516c77SSepherosa Ziehau 				    CTLFLAG_RD, &txr->hn_oactive, 0,
539715516c77SSepherosa Ziehau 				    "over active");
539815516c77SSepherosa Ziehau 			}
539915516c77SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "packets",
5400db0ac6deSCy Schubert 			    CTLFLAG_RW | CTLFLAG_STATS, &txr->hn_pkts,
540115516c77SSepherosa Ziehau 			    "# of packets transmitted");
5402dc13fee6SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "sends",
5403db0ac6deSCy Schubert 			    CTLFLAG_RW | CTLFLAG_STATS, &txr->hn_sends,
5404db0ac6deSCy Schubert 			    "# of sends");
540515516c77SSepherosa Ziehau 		}
540615516c77SSepherosa Ziehau 	}
540715516c77SSepherosa Ziehau 
540815516c77SSepherosa Ziehau 	return 0;
540915516c77SSepherosa Ziehau }
541015516c77SSepherosa Ziehau 
541115516c77SSepherosa Ziehau static void
541215516c77SSepherosa Ziehau hn_txdesc_dmamap_destroy(struct hn_txdesc *txd)
541315516c77SSepherosa Ziehau {
541415516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = txd->txr;
541515516c77SSepherosa Ziehau 
541615516c77SSepherosa Ziehau 	KASSERT(txd->m == NULL, ("still has mbuf installed"));
541715516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("still dma mapped"));
541815516c77SSepherosa Ziehau 
541915516c77SSepherosa Ziehau 	bus_dmamap_unload(txr->hn_tx_rndis_dtag, txd->rndis_pkt_dmap);
542015516c77SSepherosa Ziehau 	bus_dmamem_free(txr->hn_tx_rndis_dtag, txd->rndis_pkt,
542115516c77SSepherosa Ziehau 	    txd->rndis_pkt_dmap);
542215516c77SSepherosa Ziehau 	bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap);
542315516c77SSepherosa Ziehau }
542415516c77SSepherosa Ziehau 
542515516c77SSepherosa Ziehau static void
542625641fc7SSepherosa Ziehau hn_txdesc_gc(struct hn_tx_ring *txr, struct hn_txdesc *txd)
542725641fc7SSepherosa Ziehau {
542825641fc7SSepherosa Ziehau 
542925641fc7SSepherosa Ziehau 	KASSERT(txd->refs == 0 || txd->refs == 1,
543025641fc7SSepherosa Ziehau 	    ("invalid txd refs %d", txd->refs));
543125641fc7SSepherosa Ziehau 
543225641fc7SSepherosa Ziehau 	/* Aggregated txds will be freed by their aggregating txd. */
543325641fc7SSepherosa Ziehau 	if (txd->refs > 0 && (txd->flags & HN_TXD_FLAG_ONAGG) == 0) {
5434a0f49d67SMateusz Guzik 		int freed __diagused;
543525641fc7SSepherosa Ziehau 
543625641fc7SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
543725641fc7SSepherosa Ziehau 		KASSERT(freed, ("can't free txdesc"));
543825641fc7SSepherosa Ziehau 	}
543925641fc7SSepherosa Ziehau }
544025641fc7SSepherosa Ziehau 
544125641fc7SSepherosa Ziehau static void
544215516c77SSepherosa Ziehau hn_tx_ring_destroy(struct hn_tx_ring *txr)
544315516c77SSepherosa Ziehau {
544425641fc7SSepherosa Ziehau 	int i;
544515516c77SSepherosa Ziehau 
544615516c77SSepherosa Ziehau 	if (txr->hn_txdesc == NULL)
544715516c77SSepherosa Ziehau 		return;
544815516c77SSepherosa Ziehau 
544925641fc7SSepherosa Ziehau 	/*
545025641fc7SSepherosa Ziehau 	 * NOTE:
545125641fc7SSepherosa Ziehau 	 * Because the freeing of aggregated txds will be deferred
545225641fc7SSepherosa Ziehau 	 * to the aggregating txd, two passes are used here:
545325641fc7SSepherosa Ziehau 	 * - The first pass GCes any pending txds.  This GC is necessary,
545425641fc7SSepherosa Ziehau 	 *   since if the channels are revoked, hypervisor will not
545525641fc7SSepherosa Ziehau 	 *   deliver send-done for all pending txds.
545625641fc7SSepherosa Ziehau 	 * - The second pass frees the busdma stuffs, i.e. after all txds
545725641fc7SSepherosa Ziehau 	 *   were freed.
545825641fc7SSepherosa Ziehau 	 */
545925641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
546025641fc7SSepherosa Ziehau 		hn_txdesc_gc(txr, &txr->hn_txdesc[i]);
546125641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
546225641fc7SSepherosa Ziehau 		hn_txdesc_dmamap_destroy(&txr->hn_txdesc[i]);
546315516c77SSepherosa Ziehau 
546415516c77SSepherosa Ziehau 	if (txr->hn_tx_data_dtag != NULL)
546515516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_data_dtag);
546615516c77SSepherosa Ziehau 	if (txr->hn_tx_rndis_dtag != NULL)
546715516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_rndis_dtag);
546815516c77SSepherosa Ziehau 
546915516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
547015516c77SSepherosa Ziehau 	buf_ring_free(txr->hn_txdesc_br, M_DEVBUF);
547115516c77SSepherosa Ziehau #endif
547215516c77SSepherosa Ziehau 
547315516c77SSepherosa Ziehau 	free(txr->hn_txdesc, M_DEVBUF);
547415516c77SSepherosa Ziehau 	txr->hn_txdesc = NULL;
547515516c77SSepherosa Ziehau 
547615516c77SSepherosa Ziehau 	if (txr->hn_mbuf_br != NULL)
547715516c77SSepherosa Ziehau 		buf_ring_free(txr->hn_mbuf_br, M_DEVBUF);
547815516c77SSepherosa Ziehau 
547915516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
548015516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_txlist_spin);
548115516c77SSepherosa Ziehau #endif
548215516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_tx_lock);
548315516c77SSepherosa Ziehau }
548415516c77SSepherosa Ziehau 
548515516c77SSepherosa Ziehau static int
548615516c77SSepherosa Ziehau hn_create_tx_data(struct hn_softc *sc, int ring_cnt)
548715516c77SSepherosa Ziehau {
548815516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
548915516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
549015516c77SSepherosa Ziehau 	int i;
549115516c77SSepherosa Ziehau 
549215516c77SSepherosa Ziehau 	/*
549315516c77SSepherosa Ziehau 	 * Create TXBUF for chimney sending.
549415516c77SSepherosa Ziehau 	 *
549515516c77SSepherosa Ziehau 	 * NOTE: It is shared by all channels.
549615516c77SSepherosa Ziehau 	 */
5497*62f9bcf2SAndrew Turner 	sc->hn_chim = contigmalloc(HN_CHIM_SIZE, M_DEVBUF, M_WAITOK | M_ZERO,
5498*62f9bcf2SAndrew Turner 	    0ul, ~0ul, PAGE_SIZE, 0);
549915516c77SSepherosa Ziehau 	if (sc->hn_chim == NULL) {
550015516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate txbuf failed\n");
550115516c77SSepherosa Ziehau 		return (ENOMEM);
550215516c77SSepherosa Ziehau 	}
550315516c77SSepherosa Ziehau 
550415516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = ring_cnt;
550515516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
550615516c77SSepherosa Ziehau 
550715516c77SSepherosa Ziehau 	sc->hn_tx_ring = malloc(sizeof(struct hn_tx_ring) * sc->hn_tx_ring_cnt,
550815516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
550915516c77SSepherosa Ziehau 
551015516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(sc->hn_dev);
551115516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->hn_dev));
551215516c77SSepherosa Ziehau 
551315516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.tx sysctl tree */
551415516c77SSepherosa Ziehau 	sc->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tx",
551515516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
551615516c77SSepherosa Ziehau 
551715516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
551815516c77SSepherosa Ziehau 		int error;
551915516c77SSepherosa Ziehau 
552015516c77SSepherosa Ziehau 		error = hn_tx_ring_create(sc, i);
552115516c77SSepherosa Ziehau 		if (error)
552215516c77SSepherosa Ziehau 			return error;
552315516c77SSepherosa Ziehau 	}
552415516c77SSepherosa Ziehau 
552515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "no_txdescs",
5526db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
552715516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_no_txdescs),
552815516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of times short of TX descs");
552915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "send_failed",
5530db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
553115516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_send_failed),
553215516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of hyper-v sending failure");
553315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdma_failed",
5534db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
553515516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_txdma_failed),
553615516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX DMA failure");
5537dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_flush_failed",
5538db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
5539dc13fee6SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_flush_failed),
5540dc13fee6SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU",
5541dc13fee6SSepherosa Ziehau 	    "# of packet transmission aggregation flush failure");
554215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_collapsed",
5543db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
554415516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_collapsed),
554515516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX mbuf collapsed");
554615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney",
5547db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
554815516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney),
554915516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send");
555015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_tried",
5551db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
555215516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney_tried),
555315516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send tries");
555415516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_cnt",
555515516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_txdesc_cnt, 0,
555615516c77SSepherosa Ziehau 	    "# of total TX descs");
555715516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_chimney_max",
555815516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_chim_szmax, 0,
555915516c77SSepherosa Ziehau 	    "Chimney send packet size upper boundary");
556015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_size",
556115516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
556215516c77SSepherosa Ziehau 	    hn_chim_size_sysctl, "I", "Chimney send packet size limit");
556315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "direct_tx_size",
556415516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
556515516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_direct_tx_size),
556615516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
556715516c77SSepherosa Ziehau 	    "Size of the packet for direct transmission");
556815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "sched_tx",
556915516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
557015516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_sched_tx),
557115516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
557215516c77SSepherosa Ziehau 	    "Always schedule transmission "
557315516c77SSepherosa Ziehau 	    "instead of doing direct transmission");
557415516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_cnt",
557515516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_cnt, 0, "# created TX rings");
557615516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_inuse",
557715516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_inuse, 0, "# used TX rings");
5578dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "agg_szmax",
5579dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_agg_szmax, 0,
5580dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation size");
5581dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pktmax",
5582dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5583dc13fee6SSepherosa Ziehau 	    hn_txagg_pktmax_sysctl, "I",
5584dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation packets");
5585dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_align",
5586dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5587dc13fee6SSepherosa Ziehau 	    hn_txagg_align_sysctl, "I",
5588dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation alignment");
558915516c77SSepherosa Ziehau 
559015516c77SSepherosa Ziehau 	return 0;
559115516c77SSepherosa Ziehau }
559215516c77SSepherosa Ziehau 
559315516c77SSepherosa Ziehau static void
559415516c77SSepherosa Ziehau hn_set_chim_size(struct hn_softc *sc, int chim_size)
559515516c77SSepherosa Ziehau {
559615516c77SSepherosa Ziehau 	int i;
559715516c77SSepherosa Ziehau 
5598a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
559915516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_chim_size = chim_size;
560015516c77SSepherosa Ziehau }
560115516c77SSepherosa Ziehau 
560215516c77SSepherosa Ziehau static void
560315516c77SSepherosa Ziehau hn_set_tso_maxsize(struct hn_softc *sc, int tso_maxlen, int mtu)
560415516c77SSepherosa Ziehau {
56054db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
56069c6cae24SSepherosa Ziehau 	u_int hw_tsomax;
560715516c77SSepherosa Ziehau 	int tso_minlen;
560815516c77SSepherosa Ziehau 
56099c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
56109c6cae24SSepherosa Ziehau 
56114db5958aSJustin Hibbits 	if ((if_getcapabilities(ifp) & (IFCAP_TSO4 | IFCAP_TSO6)) == 0)
561215516c77SSepherosa Ziehau 		return;
561315516c77SSepherosa Ziehau 
561415516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_sgmin >= 2,
561515516c77SSepherosa Ziehau 	    ("invalid NDIS tso sgmin %d", sc->hn_ndis_tso_sgmin));
561615516c77SSepherosa Ziehau 	tso_minlen = sc->hn_ndis_tso_sgmin * mtu;
561715516c77SSepherosa Ziehau 
561815516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_szmax >= tso_minlen &&
561915516c77SSepherosa Ziehau 	    sc->hn_ndis_tso_szmax <= IP_MAXPACKET,
562015516c77SSepherosa Ziehau 	    ("invalid NDIS tso szmax %d", sc->hn_ndis_tso_szmax));
562115516c77SSepherosa Ziehau 
562215516c77SSepherosa Ziehau 	if (tso_maxlen < tso_minlen)
562315516c77SSepherosa Ziehau 		tso_maxlen = tso_minlen;
562415516c77SSepherosa Ziehau 	else if (tso_maxlen > IP_MAXPACKET)
562515516c77SSepherosa Ziehau 		tso_maxlen = IP_MAXPACKET;
562615516c77SSepherosa Ziehau 	if (tso_maxlen > sc->hn_ndis_tso_szmax)
562715516c77SSepherosa Ziehau 		tso_maxlen = sc->hn_ndis_tso_szmax;
56289c6cae24SSepherosa Ziehau 	hw_tsomax = tso_maxlen - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
56299c6cae24SSepherosa Ziehau 
56309c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
56314db5958aSJustin Hibbits 		if (hw_tsomax > if_gethwtsomax(sc->hn_vf_ifp))
56324db5958aSJustin Hibbits 			hw_tsomax = if_gethwtsomax(sc->hn_vf_ifp);
56339c6cae24SSepherosa Ziehau 	}
56344db5958aSJustin Hibbits 	if_sethwtsomax(ifp, hw_tsomax);
563515516c77SSepherosa Ziehau 	if (bootverbose)
56364db5958aSJustin Hibbits 		if_printf(ifp, "TSO size max %u\n", if_gethwtsomax(ifp));
563715516c77SSepherosa Ziehau }
563815516c77SSepherosa Ziehau 
563915516c77SSepherosa Ziehau static void
564015516c77SSepherosa Ziehau hn_fixup_tx_data(struct hn_softc *sc)
564115516c77SSepherosa Ziehau {
564215516c77SSepherosa Ziehau 	uint64_t csum_assist;
564315516c77SSepherosa Ziehau 	int i;
564415516c77SSepherosa Ziehau 
564515516c77SSepherosa Ziehau 	hn_set_chim_size(sc, sc->hn_chim_szmax);
564615516c77SSepherosa Ziehau 	if (hn_tx_chimney_size > 0 &&
564715516c77SSepherosa Ziehau 	    hn_tx_chimney_size < sc->hn_chim_szmax)
564815516c77SSepherosa Ziehau 		hn_set_chim_size(sc, hn_tx_chimney_size);
564915516c77SSepherosa Ziehau 
565015516c77SSepherosa Ziehau 	csum_assist = 0;
565115516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_IPCS)
565215516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP;
565315516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP4CS)
565415516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_TCP;
56552be266caSSepherosa Ziehau 	if ((sc->hn_caps & HN_CAP_UDP4CS) && hn_enable_udp4cs)
565615516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_UDP;
565715516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP6CS)
565815516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_TCP;
56592be266caSSepherosa Ziehau 	if ((sc->hn_caps & HN_CAP_UDP6CS) && hn_enable_udp6cs)
566015516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_UDP;
566115516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
566215516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_csum_assist = csum_assist;
566315516c77SSepherosa Ziehau 
566415516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_HASHVAL) {
566515516c77SSepherosa Ziehau 		/*
566615516c77SSepherosa Ziehau 		 * Support HASHVAL pktinfo on TX path.
566715516c77SSepherosa Ziehau 		 */
566815516c77SSepherosa Ziehau 		if (bootverbose)
566915516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "support HASHVAL pktinfo\n");
567015516c77SSepherosa Ziehau 		for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
567115516c77SSepherosa Ziehau 			sc->hn_tx_ring[i].hn_tx_flags |= HN_TX_FLAG_HASHVAL;
567215516c77SSepherosa Ziehau 	}
567315516c77SSepherosa Ziehau }
567415516c77SSepherosa Ziehau 
567515516c77SSepherosa Ziehau static void
5676db76829bSSepherosa Ziehau hn_fixup_rx_data(struct hn_softc *sc)
5677db76829bSSepherosa Ziehau {
5678db76829bSSepherosa Ziehau 
5679db76829bSSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_UDPHASH) {
5680db76829bSSepherosa Ziehau 		int i;
5681db76829bSSepherosa Ziehau 
5682db76829bSSepherosa Ziehau 		for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
5683db76829bSSepherosa Ziehau 			sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_UDP_HASH;
5684db76829bSSepherosa Ziehau 	}
5685db76829bSSepherosa Ziehau }
5686db76829bSSepherosa Ziehau 
5687db76829bSSepherosa Ziehau static void
568815516c77SSepherosa Ziehau hn_destroy_tx_data(struct hn_softc *sc)
568915516c77SSepherosa Ziehau {
569015516c77SSepherosa Ziehau 	int i;
569115516c77SSepherosa Ziehau 
569215516c77SSepherosa Ziehau 	if (sc->hn_chim != NULL) {
56932494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_CHIM_REF) == 0) {
5694*62f9bcf2SAndrew Turner 			contigfree(sc->hn_chim, HN_CHIM_SIZE, M_DEVBUF);
56952494d735SSepherosa Ziehau 		} else {
56962494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
56972494d735SSepherosa Ziehau 			    "chimney sending buffer is referenced");
56982494d735SSepherosa Ziehau 		}
569915516c77SSepherosa Ziehau 		sc->hn_chim = NULL;
570015516c77SSepherosa Ziehau 	}
570115516c77SSepherosa Ziehau 
570215516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt == 0)
570315516c77SSepherosa Ziehau 		return;
570415516c77SSepherosa Ziehau 
570515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
570615516c77SSepherosa Ziehau 		hn_tx_ring_destroy(&sc->hn_tx_ring[i]);
570715516c77SSepherosa Ziehau 
570815516c77SSepherosa Ziehau 	free(sc->hn_tx_ring, M_DEVBUF);
570915516c77SSepherosa Ziehau 	sc->hn_tx_ring = NULL;
571015516c77SSepherosa Ziehau 
571115516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = 0;
571215516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = 0;
571315516c77SSepherosa Ziehau }
571415516c77SSepherosa Ziehau 
571523bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
571623bf9e15SSepherosa Ziehau 
571715516c77SSepherosa Ziehau static void
571815516c77SSepherosa Ziehau hn_start_taskfunc(void *xtxr, int pending __unused)
571915516c77SSepherosa Ziehau {
572015516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
572115516c77SSepherosa Ziehau 
572215516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
572315516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
572415516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
572515516c77SSepherosa Ziehau }
572615516c77SSepherosa Ziehau 
572723bf9e15SSepherosa Ziehau static int
572823bf9e15SSepherosa Ziehau hn_start_locked(struct hn_tx_ring *txr, int len)
572923bf9e15SSepherosa Ziehau {
573023bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
57314db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
5732dc13fee6SSepherosa Ziehau 	int sched = 0;
573323bf9e15SSepherosa Ziehau 
573423bf9e15SSepherosa Ziehau 	KASSERT(hn_use_if_start,
573523bf9e15SSepherosa Ziehau 	    ("hn_start_locked is called, when if_start is disabled"));
573623bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
573723bf9e15SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
5738dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
573923bf9e15SSepherosa Ziehau 
574023bf9e15SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5741dc13fee6SSepherosa Ziehau 		return (0);
574223bf9e15SSepherosa Ziehau 
57434db5958aSJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
574423bf9e15SSepherosa Ziehau 	    IFF_DRV_RUNNING)
5745dc13fee6SSepherosa Ziehau 		return (0);
574623bf9e15SSepherosa Ziehau 
57474db5958aSJustin Hibbits 	while (!if_sendq_empty(ifp)) {
574823bf9e15SSepherosa Ziehau 		struct hn_txdesc *txd;
574923bf9e15SSepherosa Ziehau 		struct mbuf *m_head;
575023bf9e15SSepherosa Ziehau 		int error;
575123bf9e15SSepherosa Ziehau 
57524db5958aSJustin Hibbits 		m_head = if_dequeue(ifp);
575323bf9e15SSepherosa Ziehau 		if (m_head == NULL)
575423bf9e15SSepherosa Ziehau 			break;
575523bf9e15SSepherosa Ziehau 
575623bf9e15SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
575723bf9e15SSepherosa Ziehau 			/*
575823bf9e15SSepherosa Ziehau 			 * This sending could be time consuming; let callers
575923bf9e15SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
576023bf9e15SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
576123bf9e15SSepherosa Ziehau 			 */
57624db5958aSJustin Hibbits 			if_sendq_prepend(ifp, m_head);
5763dc13fee6SSepherosa Ziehau 			sched = 1;
5764dc13fee6SSepherosa Ziehau 			break;
576523bf9e15SSepherosa Ziehau 		}
576623bf9e15SSepherosa Ziehau 
5767edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
5768edd3f315SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
5769edd3f315SSepherosa Ziehau 			m_head = hn_tso_fixup(m_head);
5770edd3f315SSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5771edd3f315SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5772edd3f315SSepherosa Ziehau 				continue;
5773edd3f315SSepherosa Ziehau 			}
5774c49d47daSSepherosa Ziehau 		} else if (m_head->m_pkthdr.csum_flags &
5775c49d47daSSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP)) {
5776c49d47daSSepherosa Ziehau 			m_head = hn_set_hlen(m_head);
5777c49d47daSSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5778c49d47daSSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5779c49d47daSSepherosa Ziehau 				continue;
5780c49d47daSSepherosa Ziehau 			}
5781edd3f315SSepherosa Ziehau 		}
5782edd3f315SSepherosa Ziehau #endif
5783edd3f315SSepherosa Ziehau 
578423bf9e15SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
578523bf9e15SSepherosa Ziehau 		if (txd == NULL) {
578623bf9e15SSepherosa Ziehau 			txr->hn_no_txdescs++;
57874db5958aSJustin Hibbits 			if_sendq_prepend(ifp, m_head);
57884db5958aSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
578923bf9e15SSepherosa Ziehau 			break;
579023bf9e15SSepherosa Ziehau 		}
579123bf9e15SSepherosa Ziehau 
5792dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
579323bf9e15SSepherosa Ziehau 		if (error) {
579423bf9e15SSepherosa Ziehau 			/* Both txd and m_head are freed */
5795dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5796dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
579723bf9e15SSepherosa Ziehau 			continue;
579823bf9e15SSepherosa Ziehau 		}
579923bf9e15SSepherosa Ziehau 
5800dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5801dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5802dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5803dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5804dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
5805dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
58064db5958aSJustin Hibbits 					if_setdrvflagbits(ifp,
58074db5958aSJustin Hibbits 					    IFF_DRV_OACTIVE, 0);
5808dc13fee6SSepherosa Ziehau 					break;
5809dc13fee6SSepherosa Ziehau 				}
5810dc13fee6SSepherosa Ziehau 			} else {
5811dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
581223bf9e15SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
581323bf9e15SSepherosa Ziehau 				if (__predict_false(error)) {
581423bf9e15SSepherosa Ziehau 					/* txd is freed, but m_head is not */
58154db5958aSJustin Hibbits 					if_sendq_prepend(ifp, m_head);
58164db5958aSJustin Hibbits 					if_setdrvflagbits(ifp,
58174db5958aSJustin Hibbits 					    IFF_DRV_OACTIVE, 0);
581823bf9e15SSepherosa Ziehau 					break;
581923bf9e15SSepherosa Ziehau 				}
582023bf9e15SSepherosa Ziehau 			}
5821dc13fee6SSepherosa Ziehau 		}
5822dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5823dc13fee6SSepherosa Ziehau 		else {
5824dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5825dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5826dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5827dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5828dc13fee6SSepherosa Ziehau 		}
5829dc13fee6SSepherosa Ziehau #endif
5830dc13fee6SSepherosa Ziehau 	}
5831dc13fee6SSepherosa Ziehau 
5832dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5833dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5834dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5835dc13fee6SSepherosa Ziehau 	return (sched);
583623bf9e15SSepherosa Ziehau }
583723bf9e15SSepherosa Ziehau 
583823bf9e15SSepherosa Ziehau static void
58394db5958aSJustin Hibbits hn_start(if_t ifp)
584023bf9e15SSepherosa Ziehau {
58414db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
584223bf9e15SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[0];
584323bf9e15SSepherosa Ziehau 
584423bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
584523bf9e15SSepherosa Ziehau 		goto do_sched;
584623bf9e15SSepherosa Ziehau 
584723bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
584823bf9e15SSepherosa Ziehau 		int sched;
584923bf9e15SSepherosa Ziehau 
585023bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
585123bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
585223bf9e15SSepherosa Ziehau 		if (!sched)
585323bf9e15SSepherosa Ziehau 			return;
585423bf9e15SSepherosa Ziehau 	}
585523bf9e15SSepherosa Ziehau do_sched:
585623bf9e15SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
585723bf9e15SSepherosa Ziehau }
585823bf9e15SSepherosa Ziehau 
585915516c77SSepherosa Ziehau static void
586015516c77SSepherosa Ziehau hn_start_txeof_taskfunc(void *xtxr, int pending __unused)
586115516c77SSepherosa Ziehau {
586215516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
586315516c77SSepherosa Ziehau 
586415516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
58654db5958aSJustin Hibbits 	if_setdrvflagbits(txr->hn_sc->hn_ifp, 0, IFF_DRV_OACTIVE);
586615516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
586715516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
586815516c77SSepherosa Ziehau }
586915516c77SSepherosa Ziehau 
587023bf9e15SSepherosa Ziehau static void
587123bf9e15SSepherosa Ziehau hn_start_txeof(struct hn_tx_ring *txr)
587223bf9e15SSepherosa Ziehau {
587323bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
58744db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
587523bf9e15SSepherosa Ziehau 
587623bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
587723bf9e15SSepherosa Ziehau 
587823bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
587923bf9e15SSepherosa Ziehau 		goto do_sched;
588023bf9e15SSepherosa Ziehau 
588123bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
588223bf9e15SSepherosa Ziehau 		int sched;
588323bf9e15SSepherosa Ziehau 
58844db5958aSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
588523bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
588623bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
588723bf9e15SSepherosa Ziehau 		if (sched) {
588823bf9e15SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
588923bf9e15SSepherosa Ziehau 			    &txr->hn_tx_task);
589023bf9e15SSepherosa Ziehau 		}
589123bf9e15SSepherosa Ziehau 	} else {
589223bf9e15SSepherosa Ziehau do_sched:
589323bf9e15SSepherosa Ziehau 		/*
589423bf9e15SSepherosa Ziehau 		 * Release the OACTIVE earlier, with the hope, that
589523bf9e15SSepherosa Ziehau 		 * others could catch up.  The task will clear the
589623bf9e15SSepherosa Ziehau 		 * flag again with the hn_tx_lock to avoid possible
589723bf9e15SSepherosa Ziehau 		 * races.
589823bf9e15SSepherosa Ziehau 		 */
58994db5958aSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
590023bf9e15SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
590123bf9e15SSepherosa Ziehau 	}
590223bf9e15SSepherosa Ziehau }
590323bf9e15SSepherosa Ziehau 
590423bf9e15SSepherosa Ziehau #endif	/* HN_IFSTART_SUPPORT */
590523bf9e15SSepherosa Ziehau 
590615516c77SSepherosa Ziehau static int
590715516c77SSepherosa Ziehau hn_xmit(struct hn_tx_ring *txr, int len)
590815516c77SSepherosa Ziehau {
590915516c77SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
59104db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
591115516c77SSepherosa Ziehau 	struct mbuf *m_head;
5912dc13fee6SSepherosa Ziehau 	int sched = 0;
591315516c77SSepherosa Ziehau 
591415516c77SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
591523bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
591615516c77SSepherosa Ziehau 	KASSERT(hn_use_if_start == 0,
591715516c77SSepherosa Ziehau 	    ("hn_xmit is called, when if_start is enabled"));
591823bf9e15SSepherosa Ziehau #endif
5919dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
592015516c77SSepherosa Ziehau 
592115516c77SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5922dc13fee6SSepherosa Ziehau 		return (0);
592315516c77SSepherosa Ziehau 
59244db5958aSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0 || txr->hn_oactive)
5925dc13fee6SSepherosa Ziehau 		return (0);
592615516c77SSepherosa Ziehau 
592715516c77SSepherosa Ziehau 	while ((m_head = drbr_peek(ifp, txr->hn_mbuf_br)) != NULL) {
592815516c77SSepherosa Ziehau 		struct hn_txdesc *txd;
592915516c77SSepherosa Ziehau 		int error;
593015516c77SSepherosa Ziehau 
593115516c77SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
593215516c77SSepherosa Ziehau 			/*
593315516c77SSepherosa Ziehau 			 * This sending could be time consuming; let callers
593415516c77SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
593515516c77SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
593615516c77SSepherosa Ziehau 			 */
593715516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
5938dc13fee6SSepherosa Ziehau 			sched = 1;
5939dc13fee6SSepherosa Ziehau 			break;
594015516c77SSepherosa Ziehau 		}
594115516c77SSepherosa Ziehau 
594215516c77SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
594315516c77SSepherosa Ziehau 		if (txd == NULL) {
594415516c77SSepherosa Ziehau 			txr->hn_no_txdescs++;
594515516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
594615516c77SSepherosa Ziehau 			txr->hn_oactive = 1;
594715516c77SSepherosa Ziehau 			break;
594815516c77SSepherosa Ziehau 		}
594915516c77SSepherosa Ziehau 
5950dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
595115516c77SSepherosa Ziehau 		if (error) {
595215516c77SSepherosa Ziehau 			/* Both txd and m_head are freed; discard */
5953dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5954dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
595515516c77SSepherosa Ziehau 			drbr_advance(ifp, txr->hn_mbuf_br);
595615516c77SSepherosa Ziehau 			continue;
595715516c77SSepherosa Ziehau 		}
595815516c77SSepherosa Ziehau 
5959dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5960dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5961dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5962dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5963dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
596415516c77SSepherosa Ziehau 				if (__predict_false(error)) {
596515516c77SSepherosa Ziehau 					txr->hn_oactive = 1;
596615516c77SSepherosa Ziehau 					break;
596715516c77SSepherosa Ziehau 				}
5968dc13fee6SSepherosa Ziehau 			} else {
5969dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
5970dc13fee6SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
5971dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5972dc13fee6SSepherosa Ziehau 					/* txd is freed, but m_head is not */
5973dc13fee6SSepherosa Ziehau 					drbr_putback(ifp, txr->hn_mbuf_br,
5974dc13fee6SSepherosa Ziehau 					    m_head);
5975dc13fee6SSepherosa Ziehau 					txr->hn_oactive = 1;
5976dc13fee6SSepherosa Ziehau 					break;
5977dc13fee6SSepherosa Ziehau 				}
5978dc13fee6SSepherosa Ziehau 			}
5979dc13fee6SSepherosa Ziehau 		}
5980dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5981dc13fee6SSepherosa Ziehau 		else {
5982dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5983dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5984dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5985dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5986dc13fee6SSepherosa Ziehau 		}
5987dc13fee6SSepherosa Ziehau #endif
598815516c77SSepherosa Ziehau 
598915516c77SSepherosa Ziehau 		/* Sent */
599015516c77SSepherosa Ziehau 		drbr_advance(ifp, txr->hn_mbuf_br);
599115516c77SSepherosa Ziehau 	}
5992dc13fee6SSepherosa Ziehau 
5993dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5994dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5995dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5996dc13fee6SSepherosa Ziehau 	return (sched);
599715516c77SSepherosa Ziehau }
599815516c77SSepherosa Ziehau 
599915516c77SSepherosa Ziehau static int
60004db5958aSJustin Hibbits hn_transmit(if_t ifp, struct mbuf *m)
600115516c77SSepherosa Ziehau {
60024db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
600315516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
600415516c77SSepherosa Ziehau 	int error, idx = 0;
600515516c77SSepherosa Ziehau 
60069c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
60079c6cae24SSepherosa Ziehau 		struct rm_priotracker pt;
60089c6cae24SSepherosa Ziehau 
60099c6cae24SSepherosa Ziehau 		rm_rlock(&sc->hn_vf_lock, &pt);
60109c6cae24SSepherosa Ziehau 		if (__predict_true(sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
60119c6cae24SSepherosa Ziehau 			struct mbuf *m_bpf = NULL;
60129c6cae24SSepherosa Ziehau 			int obytes, omcast;
60139c6cae24SSepherosa Ziehau 
60149c6cae24SSepherosa Ziehau 			obytes = m->m_pkthdr.len;
60157898a1f4SEric van Gyzen 			omcast = (m->m_flags & M_MCAST) != 0;
60169c6cae24SSepherosa Ziehau 
60179c6cae24SSepherosa Ziehau 			if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF) {
60184db5958aSJustin Hibbits 				if (bpf_peers_present(if_getbpf(ifp))) {
60199c6cae24SSepherosa Ziehau 					m_bpf = m_copypacket(m, M_NOWAIT);
60209c6cae24SSepherosa Ziehau 					if (m_bpf == NULL) {
60219c6cae24SSepherosa Ziehau 						/*
60229c6cae24SSepherosa Ziehau 						 * Failed to grab a shallow
60239c6cae24SSepherosa Ziehau 						 * copy; tap now.
60249c6cae24SSepherosa Ziehau 						 */
60259c6cae24SSepherosa Ziehau 						ETHER_BPF_MTAP(ifp, m);
60269c6cae24SSepherosa Ziehau 					}
60279c6cae24SSepherosa Ziehau 				}
60289c6cae24SSepherosa Ziehau 			} else {
60299c6cae24SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, m);
60309c6cae24SSepherosa Ziehau 			}
60319c6cae24SSepherosa Ziehau 
60324db5958aSJustin Hibbits 			error = if_transmit(sc->hn_vf_ifp, m);
60339c6cae24SSepherosa Ziehau 			rm_runlock(&sc->hn_vf_lock, &pt);
60349c6cae24SSepherosa Ziehau 
60359c6cae24SSepherosa Ziehau 			if (m_bpf != NULL) {
60369c6cae24SSepherosa Ziehau 				if (!error)
60379c6cae24SSepherosa Ziehau 					ETHER_BPF_MTAP(ifp, m_bpf);
60389c6cae24SSepherosa Ziehau 				m_freem(m_bpf);
60399c6cae24SSepherosa Ziehau 			}
60409c6cae24SSepherosa Ziehau 
60419c6cae24SSepherosa Ziehau 			if (error == ENOBUFS) {
60429c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
60439c6cae24SSepherosa Ziehau 			} else if (error) {
60449c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
60459c6cae24SSepherosa Ziehau 			} else {
60469c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
60479c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OBYTES, obytes);
60489c6cae24SSepherosa Ziehau 				if (omcast) {
60499c6cae24SSepherosa Ziehau 					if_inc_counter(ifp, IFCOUNTER_OMCASTS,
60509c6cae24SSepherosa Ziehau 					    omcast);
60519c6cae24SSepherosa Ziehau 				}
60529c6cae24SSepherosa Ziehau 			}
60539c6cae24SSepherosa Ziehau 			return (error);
60549c6cae24SSepherosa Ziehau 		}
60559c6cae24SSepherosa Ziehau 		rm_runlock(&sc->hn_vf_lock, &pt);
60569c6cae24SSepherosa Ziehau 	}
60579c6cae24SSepherosa Ziehau 
6058edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
6059edd3f315SSepherosa Ziehau 	/*
6060c49d47daSSepherosa Ziehau 	 * Perform TSO packet header fixup or get l2/l3 header length now,
6061c49d47daSSepherosa Ziehau 	 * since packet headers should be cache-hot.
6062edd3f315SSepherosa Ziehau 	 */
6063edd3f315SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
6064edd3f315SSepherosa Ziehau 		m = hn_tso_fixup(m);
6065edd3f315SSepherosa Ziehau 		if (__predict_false(m == NULL)) {
6066edd3f315SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
6067edd3f315SSepherosa Ziehau 			return EIO;
6068edd3f315SSepherosa Ziehau 		}
6069c49d47daSSepherosa Ziehau 	} else if (m->m_pkthdr.csum_flags &
6070c49d47daSSepherosa Ziehau 	    (CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP)) {
6071c49d47daSSepherosa Ziehau 		m = hn_set_hlen(m);
6072c49d47daSSepherosa Ziehau 		if (__predict_false(m == NULL)) {
6073c49d47daSSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
6074c49d47daSSepherosa Ziehau 			return EIO;
6075c49d47daSSepherosa Ziehau 		}
6076edd3f315SSepherosa Ziehau 	}
6077edd3f315SSepherosa Ziehau #endif
6078edd3f315SSepherosa Ziehau 
607915516c77SSepherosa Ziehau 	/*
608015516c77SSepherosa Ziehau 	 * Select the TX ring based on flowid
608115516c77SSepherosa Ziehau 	 */
608234d68912SSepherosa Ziehau 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
608334d68912SSepherosa Ziehau #ifdef RSS
608434d68912SSepherosa Ziehau 		uint32_t bid;
608534d68912SSepherosa Ziehau 
608634d68912SSepherosa Ziehau 		if (rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m),
608734d68912SSepherosa Ziehau 		    &bid) == 0)
608834d68912SSepherosa Ziehau 			idx = bid % sc->hn_tx_ring_inuse;
608934d68912SSepherosa Ziehau 		else
609034d68912SSepherosa Ziehau #endif
6091cc0c6ebcSSepherosa Ziehau 		{
6092cc0c6ebcSSepherosa Ziehau #if defined(INET6) || defined(INET)
6093cc0c6ebcSSepherosa Ziehau 			int tcpsyn = 0;
6094cc0c6ebcSSepherosa Ziehau 
6095cc0c6ebcSSepherosa Ziehau 			if (m->m_pkthdr.len < 128 &&
6096cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags &
6097cc0c6ebcSSepherosa Ziehau 			     (CSUM_IP_TCP | CSUM_IP6_TCP)) &&
6098cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
6099cc0c6ebcSSepherosa Ziehau 				m = hn_check_tcpsyn(m, &tcpsyn);
6100cc0c6ebcSSepherosa Ziehau 				if (__predict_false(m == NULL)) {
6101cc0c6ebcSSepherosa Ziehau 					if_inc_counter(ifp,
6102cc0c6ebcSSepherosa Ziehau 					    IFCOUNTER_OERRORS, 1);
6103cc0c6ebcSSepherosa Ziehau 					return (EIO);
6104cc0c6ebcSSepherosa Ziehau 				}
6105cc0c6ebcSSepherosa Ziehau 			}
6106cc0c6ebcSSepherosa Ziehau #else
6107cc0c6ebcSSepherosa Ziehau 			const int tcpsyn = 0;
6108cc0c6ebcSSepherosa Ziehau #endif
6109cc0c6ebcSSepherosa Ziehau 			if (tcpsyn)
6110cc0c6ebcSSepherosa Ziehau 				idx = 0;
6111cc0c6ebcSSepherosa Ziehau 			else
611215516c77SSepherosa Ziehau 				idx = m->m_pkthdr.flowid % sc->hn_tx_ring_inuse;
611334d68912SSepherosa Ziehau 		}
6114cc0c6ebcSSepherosa Ziehau 	}
611515516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[idx];
611615516c77SSepherosa Ziehau 
611715516c77SSepherosa Ziehau 	error = drbr_enqueue(ifp, txr->hn_mbuf_br, m);
611815516c77SSepherosa Ziehau 	if (error) {
611915516c77SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
612015516c77SSepherosa Ziehau 		return error;
612115516c77SSepherosa Ziehau 	}
612215516c77SSepherosa Ziehau 
612315516c77SSepherosa Ziehau 	if (txr->hn_oactive)
612415516c77SSepherosa Ziehau 		return 0;
612515516c77SSepherosa Ziehau 
612615516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
612715516c77SSepherosa Ziehau 		goto do_sched;
612815516c77SSepherosa Ziehau 
612915516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
613015516c77SSepherosa Ziehau 		int sched;
613115516c77SSepherosa Ziehau 
613215516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
613315516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
613415516c77SSepherosa Ziehau 		if (!sched)
613515516c77SSepherosa Ziehau 			return 0;
613615516c77SSepherosa Ziehau 	}
613715516c77SSepherosa Ziehau do_sched:
613815516c77SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
613915516c77SSepherosa Ziehau 	return 0;
614015516c77SSepherosa Ziehau }
614115516c77SSepherosa Ziehau 
614215516c77SSepherosa Ziehau static void
614315516c77SSepherosa Ziehau hn_tx_ring_qflush(struct hn_tx_ring *txr)
614415516c77SSepherosa Ziehau {
614515516c77SSepherosa Ziehau 	struct mbuf *m;
614615516c77SSepherosa Ziehau 
614715516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
614815516c77SSepherosa Ziehau 	while ((m = buf_ring_dequeue_sc(txr->hn_mbuf_br)) != NULL)
614915516c77SSepherosa Ziehau 		m_freem(m);
615015516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
615115516c77SSepherosa Ziehau }
615215516c77SSepherosa Ziehau 
615315516c77SSepherosa Ziehau static void
61544db5958aSJustin Hibbits hn_xmit_qflush(if_t ifp)
615515516c77SSepherosa Ziehau {
61564db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
61579c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
615815516c77SSepherosa Ziehau 	int i;
615915516c77SSepherosa Ziehau 
616015516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
616115516c77SSepherosa Ziehau 		hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
616215516c77SSepherosa Ziehau 	if_qflush(ifp);
61639c6cae24SSepherosa Ziehau 
61649c6cae24SSepherosa Ziehau 	rm_rlock(&sc->hn_vf_lock, &pt);
61659c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
61664db5958aSJustin Hibbits 		if_qflush(sc->hn_vf_ifp);
61679c6cae24SSepherosa Ziehau 	rm_runlock(&sc->hn_vf_lock, &pt);
616815516c77SSepherosa Ziehau }
616915516c77SSepherosa Ziehau 
617015516c77SSepherosa Ziehau static void
617115516c77SSepherosa Ziehau hn_xmit_txeof(struct hn_tx_ring *txr)
617215516c77SSepherosa Ziehau {
617315516c77SSepherosa Ziehau 
617415516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
617515516c77SSepherosa Ziehau 		goto do_sched;
617615516c77SSepherosa Ziehau 
617715516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
617815516c77SSepherosa Ziehau 		int sched;
617915516c77SSepherosa Ziehau 
618015516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
618115516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
618215516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
618315516c77SSepherosa Ziehau 		if (sched) {
618415516c77SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
618515516c77SSepherosa Ziehau 			    &txr->hn_tx_task);
618615516c77SSepherosa Ziehau 		}
618715516c77SSepherosa Ziehau 	} else {
618815516c77SSepherosa Ziehau do_sched:
618915516c77SSepherosa Ziehau 		/*
619015516c77SSepherosa Ziehau 		 * Release the oactive earlier, with the hope, that
619115516c77SSepherosa Ziehau 		 * others could catch up.  The task will clear the
619215516c77SSepherosa Ziehau 		 * oactive again with the hn_tx_lock to avoid possible
619315516c77SSepherosa Ziehau 		 * races.
619415516c77SSepherosa Ziehau 		 */
619515516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
619615516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
619715516c77SSepherosa Ziehau 	}
619815516c77SSepherosa Ziehau }
619915516c77SSepherosa Ziehau 
620015516c77SSepherosa Ziehau static void
620115516c77SSepherosa Ziehau hn_xmit_taskfunc(void *xtxr, int pending __unused)
620215516c77SSepherosa Ziehau {
620315516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
620415516c77SSepherosa Ziehau 
620515516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
620615516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
620715516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
620815516c77SSepherosa Ziehau }
620915516c77SSepherosa Ziehau 
621015516c77SSepherosa Ziehau static void
621115516c77SSepherosa Ziehau hn_xmit_txeof_taskfunc(void *xtxr, int pending __unused)
621215516c77SSepherosa Ziehau {
621315516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
621415516c77SSepherosa Ziehau 
621515516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
621615516c77SSepherosa Ziehau 	txr->hn_oactive = 0;
621715516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
621815516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
621915516c77SSepherosa Ziehau }
622015516c77SSepherosa Ziehau 
622115516c77SSepherosa Ziehau static int
622215516c77SSepherosa Ziehau hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
622315516c77SSepherosa Ziehau {
622415516c77SSepherosa Ziehau 	struct vmbus_chan_br cbr;
622515516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
622615516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = NULL;
622715516c77SSepherosa Ziehau 	int idx, error;
622815516c77SSepherosa Ziehau 
622915516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
623015516c77SSepherosa Ziehau 
623115516c77SSepherosa Ziehau 	/*
623215516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
623315516c77SSepherosa Ziehau 	 */
623415516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
623515516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
623615516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
623715516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
623815516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED) == 0,
623915516c77SSepherosa Ziehau 	    ("RX ring %d already attached", idx));
624015516c77SSepherosa Ziehau 	rxr->hn_rx_flags |= HN_RX_FLAG_ATTACHED;
62413ab0fea1SDexuan Cui 	rxr->hn_chan = chan;
624215516c77SSepherosa Ziehau 
624315516c77SSepherosa Ziehau 	if (bootverbose) {
624415516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "link RX ring %d to chan%u\n",
624515516c77SSepherosa Ziehau 		    idx, vmbus_chan_id(chan));
624615516c77SSepherosa Ziehau 	}
624715516c77SSepherosa Ziehau 
624815516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
624915516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[idx];
625015516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED) == 0,
625115516c77SSepherosa Ziehau 		    ("TX ring %d already attached", idx));
625215516c77SSepherosa Ziehau 		txr->hn_tx_flags |= HN_TX_FLAG_ATTACHED;
625315516c77SSepherosa Ziehau 
625415516c77SSepherosa Ziehau 		txr->hn_chan = chan;
625515516c77SSepherosa Ziehau 		if (bootverbose) {
625615516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "link TX ring %d to chan%u\n",
625715516c77SSepherosa Ziehau 			    idx, vmbus_chan_id(chan));
625815516c77SSepherosa Ziehau 		}
625915516c77SSepherosa Ziehau 	}
626015516c77SSepherosa Ziehau 
626115516c77SSepherosa Ziehau 	/* Bind this channel to a proper CPU. */
62620e11868dSSepherosa Ziehau 	vmbus_chan_cpu_set(chan, HN_RING_IDX2CPU(sc, idx));
626315516c77SSepherosa Ziehau 
626415516c77SSepherosa Ziehau 	/*
626515516c77SSepherosa Ziehau 	 * Open this channel
626615516c77SSepherosa Ziehau 	 */
626715516c77SSepherosa Ziehau 	cbr.cbr = rxr->hn_br;
6268*62f9bcf2SAndrew Turner 	cbr.cbr_paddr = pmap_kextract((vm_offset_t)rxr->hn_br);
626915516c77SSepherosa Ziehau 	cbr.cbr_txsz = HN_TXBR_SIZE;
627015516c77SSepherosa Ziehau 	cbr.cbr_rxsz = HN_RXBR_SIZE;
627115516c77SSepherosa Ziehau 	error = vmbus_chan_open_br(chan, &cbr, NULL, 0, hn_chan_callback, rxr);
627215516c77SSepherosa Ziehau 	if (error) {
627371e8ac56SSepherosa Ziehau 		if (error == EISCONN) {
627471e8ac56SSepherosa Ziehau 			if_printf(sc->hn_ifp, "bufring is connected after "
627571e8ac56SSepherosa Ziehau 			    "chan%u open failure\n", vmbus_chan_id(chan));
627671e8ac56SSepherosa Ziehau 			rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
627771e8ac56SSepherosa Ziehau 		} else {
627815516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "open chan%u failed: %d\n",
627915516c77SSepherosa Ziehau 			    vmbus_chan_id(chan), error);
628071e8ac56SSepherosa Ziehau 		}
628115516c77SSepherosa Ziehau 	}
628215516c77SSepherosa Ziehau 	return (error);
628315516c77SSepherosa Ziehau }
628415516c77SSepherosa Ziehau 
628515516c77SSepherosa Ziehau static void
628615516c77SSepherosa Ziehau hn_chan_detach(struct hn_softc *sc, struct vmbus_channel *chan)
628715516c77SSepherosa Ziehau {
628815516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
62892494d735SSepherosa Ziehau 	int idx, error;
629015516c77SSepherosa Ziehau 
629115516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
629215516c77SSepherosa Ziehau 
629315516c77SSepherosa Ziehau 	/*
629415516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
629515516c77SSepherosa Ziehau 	 */
629615516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
629715516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
629815516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
629915516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
630015516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED),
630115516c77SSepherosa Ziehau 	    ("RX ring %d is not attached", idx));
630215516c77SSepherosa Ziehau 	rxr->hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
630315516c77SSepherosa Ziehau 
630415516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
630515516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[idx];
630615516c77SSepherosa Ziehau 
630715516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED),
630815516c77SSepherosa Ziehau 		    ("TX ring %d is not attached attached", idx));
630915516c77SSepherosa Ziehau 		txr->hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
631015516c77SSepherosa Ziehau 	}
631115516c77SSepherosa Ziehau 
631215516c77SSepherosa Ziehau 	/*
631315516c77SSepherosa Ziehau 	 * Close this channel.
631415516c77SSepherosa Ziehau 	 *
631515516c77SSepherosa Ziehau 	 * NOTE:
631615516c77SSepherosa Ziehau 	 * Channel closing does _not_ destroy the target channel.
631715516c77SSepherosa Ziehau 	 */
63182494d735SSepherosa Ziehau 	error = vmbus_chan_close_direct(chan);
63192494d735SSepherosa Ziehau 	if (error == EISCONN) {
6320aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u bufring is connected "
6321aa1a2adcSSepherosa Ziehau 		    "after being closed\n", vmbus_chan_id(chan));
63222494d735SSepherosa Ziehau 		rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
63232494d735SSepherosa Ziehau 	} else if (error) {
6324aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u close failed: %d\n",
6325aa1a2adcSSepherosa Ziehau 		    vmbus_chan_id(chan), error);
63262494d735SSepherosa Ziehau 	}
632715516c77SSepherosa Ziehau }
632815516c77SSepherosa Ziehau 
632915516c77SSepherosa Ziehau static int
633015516c77SSepherosa Ziehau hn_attach_subchans(struct hn_softc *sc)
633115516c77SSepherosa Ziehau {
633215516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
633315516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
633415516c77SSepherosa Ziehau 	int i, error = 0;
633515516c77SSepherosa Ziehau 
633671e8ac56SSepherosa Ziehau 	KASSERT(subchan_cnt > 0, ("no sub-channels"));
633715516c77SSepherosa Ziehau 
633815516c77SSepherosa Ziehau 	/* Attach the sub-channels. */
633915516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
634015516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i) {
634171e8ac56SSepherosa Ziehau 		int error1;
634271e8ac56SSepherosa Ziehau 
634371e8ac56SSepherosa Ziehau 		error1 = hn_chan_attach(sc, subchans[i]);
634471e8ac56SSepherosa Ziehau 		if (error1) {
634571e8ac56SSepherosa Ziehau 			error = error1;
634671e8ac56SSepherosa Ziehau 			/* Move on; all channels will be detached later. */
634771e8ac56SSepherosa Ziehau 		}
634815516c77SSepherosa Ziehau 	}
634915516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
635015516c77SSepherosa Ziehau 
635115516c77SSepherosa Ziehau 	if (error) {
635215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "sub-channels attach failed: %d\n", error);
635315516c77SSepherosa Ziehau 	} else {
635415516c77SSepherosa Ziehau 		if (bootverbose) {
635515516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "%d sub-channels attached\n",
635615516c77SSepherosa Ziehau 			    subchan_cnt);
635715516c77SSepherosa Ziehau 		}
635815516c77SSepherosa Ziehau 	}
635915516c77SSepherosa Ziehau 	return (error);
636015516c77SSepherosa Ziehau }
636115516c77SSepherosa Ziehau 
636215516c77SSepherosa Ziehau static void
636315516c77SSepherosa Ziehau hn_detach_allchans(struct hn_softc *sc)
636415516c77SSepherosa Ziehau {
636515516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
636615516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
636715516c77SSepherosa Ziehau 	int i;
636815516c77SSepherosa Ziehau 
636915516c77SSepherosa Ziehau 	if (subchan_cnt == 0)
637015516c77SSepherosa Ziehau 		goto back;
637115516c77SSepherosa Ziehau 
637215516c77SSepherosa Ziehau 	/* Detach the sub-channels. */
637315516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
637415516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i)
637515516c77SSepherosa Ziehau 		hn_chan_detach(sc, subchans[i]);
637615516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
637715516c77SSepherosa Ziehau 
637815516c77SSepherosa Ziehau back:
637915516c77SSepherosa Ziehau 	/*
638015516c77SSepherosa Ziehau 	 * Detach the primary channel, _after_ all sub-channels
638115516c77SSepherosa Ziehau 	 * are detached.
638215516c77SSepherosa Ziehau 	 */
638315516c77SSepherosa Ziehau 	hn_chan_detach(sc, sc->hn_prichan);
638415516c77SSepherosa Ziehau 
638515516c77SSepherosa Ziehau 	/* Wait for sub-channels to be destroyed, if any. */
638615516c77SSepherosa Ziehau 	vmbus_subchan_drain(sc->hn_prichan);
638715516c77SSepherosa Ziehau 
638815516c77SSepherosa Ziehau #ifdef INVARIANTS
638915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
639015516c77SSepherosa Ziehau 		KASSERT((sc->hn_rx_ring[i].hn_rx_flags &
639115516c77SSepherosa Ziehau 		    HN_RX_FLAG_ATTACHED) == 0,
639215516c77SSepherosa Ziehau 		    ("%dth RX ring is still attached", i));
639315516c77SSepherosa Ziehau 	}
639415516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
639515516c77SSepherosa Ziehau 		KASSERT((sc->hn_tx_ring[i].hn_tx_flags &
639615516c77SSepherosa Ziehau 		    HN_TX_FLAG_ATTACHED) == 0,
639715516c77SSepherosa Ziehau 		    ("%dth TX ring is still attached", i));
639815516c77SSepherosa Ziehau 	}
639915516c77SSepherosa Ziehau #endif
640015516c77SSepherosa Ziehau }
640115516c77SSepherosa Ziehau 
640215516c77SSepherosa Ziehau static int
640315516c77SSepherosa Ziehau hn_synth_alloc_subchans(struct hn_softc *sc, int *nsubch)
640415516c77SSepherosa Ziehau {
640515516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
640615516c77SSepherosa Ziehau 	int nchan, rxr_cnt, error;
640715516c77SSepherosa Ziehau 
640815516c77SSepherosa Ziehau 	nchan = *nsubch + 1;
640915516c77SSepherosa Ziehau 	if (nchan == 1) {
641015516c77SSepherosa Ziehau 		/*
641115516c77SSepherosa Ziehau 		 * Multiple RX/TX rings are not requested.
641215516c77SSepherosa Ziehau 		 */
641315516c77SSepherosa Ziehau 		*nsubch = 0;
641415516c77SSepherosa Ziehau 		return (0);
641515516c77SSepherosa Ziehau 	}
641615516c77SSepherosa Ziehau 
641715516c77SSepherosa Ziehau 	/*
641815516c77SSepherosa Ziehau 	 * Query RSS capabilities, e.g. # of RX rings, and # of indirect
641915516c77SSepherosa Ziehau 	 * table entries.
642015516c77SSepherosa Ziehau 	 */
642115516c77SSepherosa Ziehau 	error = hn_rndis_query_rsscaps(sc, &rxr_cnt);
642215516c77SSepherosa Ziehau 	if (error) {
642315516c77SSepherosa Ziehau 		/* No RSS; this is benign. */
642415516c77SSepherosa Ziehau 		*nsubch = 0;
642515516c77SSepherosa Ziehau 		return (0);
642615516c77SSepherosa Ziehau 	}
642715516c77SSepherosa Ziehau 	if (bootverbose) {
642815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
642915516c77SSepherosa Ziehau 		    rxr_cnt, nchan);
643015516c77SSepherosa Ziehau 	}
643115516c77SSepherosa Ziehau 
643215516c77SSepherosa Ziehau 	if (nchan > rxr_cnt)
643315516c77SSepherosa Ziehau 		nchan = rxr_cnt;
643415516c77SSepherosa Ziehau 	if (nchan == 1) {
643515516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "only 1 channel is supported, no vRSS\n");
643615516c77SSepherosa Ziehau 		*nsubch = 0;
643715516c77SSepherosa Ziehau 		return (0);
643815516c77SSepherosa Ziehau 	}
643915516c77SSepherosa Ziehau 
644015516c77SSepherosa Ziehau 	/*
644115516c77SSepherosa Ziehau 	 * Allocate sub-channels from NVS.
644215516c77SSepherosa Ziehau 	 */
644315516c77SSepherosa Ziehau 	*nsubch = nchan - 1;
644415516c77SSepherosa Ziehau 	error = hn_nvs_alloc_subchans(sc, nsubch);
644515516c77SSepherosa Ziehau 	if (error || *nsubch == 0) {
644615516c77SSepherosa Ziehau 		/* Failed to allocate sub-channels. */
644715516c77SSepherosa Ziehau 		*nsubch = 0;
644815516c77SSepherosa Ziehau 		return (0);
644915516c77SSepherosa Ziehau 	}
645015516c77SSepherosa Ziehau 
645115516c77SSepherosa Ziehau 	/*
645215516c77SSepherosa Ziehau 	 * Wait for all sub-channels to become ready before moving on.
645315516c77SSepherosa Ziehau 	 */
645415516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, *nsubch);
645515516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, *nsubch);
645615516c77SSepherosa Ziehau 	return (0);
645715516c77SSepherosa Ziehau }
645815516c77SSepherosa Ziehau 
64592494d735SSepherosa Ziehau static bool
64602494d735SSepherosa Ziehau hn_synth_attachable(const struct hn_softc *sc)
64612494d735SSepherosa Ziehau {
64622494d735SSepherosa Ziehau 	int i;
64632494d735SSepherosa Ziehau 
64642494d735SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_ERRORS)
64652494d735SSepherosa Ziehau 		return (false);
64662494d735SSepherosa Ziehau 
64672494d735SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
64682494d735SSepherosa Ziehau 		const struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
64692494d735SSepherosa Ziehau 
64702494d735SSepherosa Ziehau 		if (rxr->hn_rx_flags & HN_RX_FLAG_BR_REF)
64712494d735SSepherosa Ziehau 			return (false);
64722494d735SSepherosa Ziehau 	}
64732494d735SSepherosa Ziehau 	return (true);
64742494d735SSepherosa Ziehau }
64752494d735SSepherosa Ziehau 
6476b3b75d9cSSepherosa Ziehau /*
6477b3b75d9cSSepherosa Ziehau  * Make sure that the RX filter is zero after the successful
6478b3b75d9cSSepherosa Ziehau  * RNDIS initialization.
6479b3b75d9cSSepherosa Ziehau  *
6480b3b75d9cSSepherosa Ziehau  * NOTE:
6481b3b75d9cSSepherosa Ziehau  * Under certain conditions on certain versions of Hyper-V,
6482b3b75d9cSSepherosa Ziehau  * the RNDIS rxfilter is _not_ zero on the hypervisor side
6483b3b75d9cSSepherosa Ziehau  * after the successful RNDIS initialization, which breaks
6484b3b75d9cSSepherosa Ziehau  * the assumption of any following code (well, it breaks the
6485b3b75d9cSSepherosa Ziehau  * RNDIS API contract actually).  Clear the RNDIS rxfilter
6486b3b75d9cSSepherosa Ziehau  * explicitly, drain packets sneaking through, and drain the
6487b3b75d9cSSepherosa Ziehau  * interrupt taskqueues scheduled due to the stealth packets.
6488b3b75d9cSSepherosa Ziehau  */
6489b3b75d9cSSepherosa Ziehau static void
6490b3b75d9cSSepherosa Ziehau hn_rndis_init_fixat(struct hn_softc *sc, int nchan)
6491b3b75d9cSSepherosa Ziehau {
6492b3b75d9cSSepherosa Ziehau 
6493b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
6494b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, nchan);
6495b3b75d9cSSepherosa Ziehau }
6496b3b75d9cSSepherosa Ziehau 
649715516c77SSepherosa Ziehau static int
649815516c77SSepherosa Ziehau hn_synth_attach(struct hn_softc *sc, int mtu)
649915516c77SSepherosa Ziehau {
650071e8ac56SSepherosa Ziehau #define ATTACHED_NVS		0x0002
650171e8ac56SSepherosa Ziehau #define ATTACHED_RNDIS		0x0004
650271e8ac56SSepherosa Ziehau 
650315516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
6504b3b75d9cSSepherosa Ziehau 	int error, nsubch, nchan = 1, i, rndis_inited;
650571e8ac56SSepherosa Ziehau 	uint32_t old_caps, attached = 0;
650615516c77SSepherosa Ziehau 
650715516c77SSepherosa Ziehau 	KASSERT((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0,
650815516c77SSepherosa Ziehau 	    ("synthetic parts were attached"));
650915516c77SSepherosa Ziehau 
65102494d735SSepherosa Ziehau 	if (!hn_synth_attachable(sc))
65112494d735SSepherosa Ziehau 		return (ENXIO);
65122494d735SSepherosa Ziehau 
651315516c77SSepherosa Ziehau 	/* Save capabilities for later verification. */
651415516c77SSepherosa Ziehau 	old_caps = sc->hn_caps;
651515516c77SSepherosa Ziehau 	sc->hn_caps = 0;
651615516c77SSepherosa Ziehau 
651715516c77SSepherosa Ziehau 	/* Clear RSS stuffs. */
651815516c77SSepherosa Ziehau 	sc->hn_rss_ind_size = 0;
651915516c77SSepherosa Ziehau 	sc->hn_rss_hash = 0;
6520642ec226SSepherosa Ziehau 	sc->hn_rss_hcap = 0;
652115516c77SSepherosa Ziehau 
652215516c77SSepherosa Ziehau 	/*
652315516c77SSepherosa Ziehau 	 * Attach the primary channel _before_ attaching NVS and RNDIS.
652415516c77SSepherosa Ziehau 	 */
652515516c77SSepherosa Ziehau 	error = hn_chan_attach(sc, sc->hn_prichan);
652615516c77SSepherosa Ziehau 	if (error)
652771e8ac56SSepherosa Ziehau 		goto failed;
652815516c77SSepherosa Ziehau 
652915516c77SSepherosa Ziehau 	/*
653015516c77SSepherosa Ziehau 	 * Attach NVS.
653115516c77SSepherosa Ziehau 	 */
653215516c77SSepherosa Ziehau 	error = hn_nvs_attach(sc, mtu);
653315516c77SSepherosa Ziehau 	if (error)
653471e8ac56SSepherosa Ziehau 		goto failed;
653571e8ac56SSepherosa Ziehau 	attached |= ATTACHED_NVS;
653615516c77SSepherosa Ziehau 
653715516c77SSepherosa Ziehau 	/*
653815516c77SSepherosa Ziehau 	 * Attach RNDIS _after_ NVS is attached.
653915516c77SSepherosa Ziehau 	 */
6540b3b75d9cSSepherosa Ziehau 	error = hn_rndis_attach(sc, mtu, &rndis_inited);
6541b3b75d9cSSepherosa Ziehau 	if (rndis_inited)
6542b3b75d9cSSepherosa Ziehau 		attached |= ATTACHED_RNDIS;
654315516c77SSepherosa Ziehau 	if (error)
654471e8ac56SSepherosa Ziehau 		goto failed;
654515516c77SSepherosa Ziehau 
654615516c77SSepherosa Ziehau 	/*
654715516c77SSepherosa Ziehau 	 * Make sure capabilities are not changed.
654815516c77SSepherosa Ziehau 	 */
654915516c77SSepherosa Ziehau 	if (device_is_attached(sc->hn_dev) && old_caps != sc->hn_caps) {
655015516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "caps mismatch old 0x%08x, new 0x%08x\n",
655115516c77SSepherosa Ziehau 		    old_caps, sc->hn_caps);
655271e8ac56SSepherosa Ziehau 		error = ENXIO;
655371e8ac56SSepherosa Ziehau 		goto failed;
655415516c77SSepherosa Ziehau 	}
655515516c77SSepherosa Ziehau 
655615516c77SSepherosa Ziehau 	/*
655715516c77SSepherosa Ziehau 	 * Allocate sub-channels for multi-TX/RX rings.
655815516c77SSepherosa Ziehau 	 *
655915516c77SSepherosa Ziehau 	 * NOTE:
656015516c77SSepherosa Ziehau 	 * The # of RX rings that can be used is equivalent to the # of
656115516c77SSepherosa Ziehau 	 * channels to be requested.
656215516c77SSepherosa Ziehau 	 */
656315516c77SSepherosa Ziehau 	nsubch = sc->hn_rx_ring_cnt - 1;
656415516c77SSepherosa Ziehau 	error = hn_synth_alloc_subchans(sc, &nsubch);
656515516c77SSepherosa Ziehau 	if (error)
656671e8ac56SSepherosa Ziehau 		goto failed;
656771e8ac56SSepherosa Ziehau 	/* NOTE: _Full_ synthetic parts detach is required now. */
656871e8ac56SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_SYNTH_ATTACHED;
656915516c77SSepherosa Ziehau 
657071e8ac56SSepherosa Ziehau 	/*
657171e8ac56SSepherosa Ziehau 	 * Set the # of TX/RX rings that could be used according to
657271e8ac56SSepherosa Ziehau 	 * the # of channels that NVS offered.
657371e8ac56SSepherosa Ziehau 	 */
657415516c77SSepherosa Ziehau 	nchan = nsubch + 1;
657571e8ac56SSepherosa Ziehau 	hn_set_ring_inuse(sc, nchan);
657615516c77SSepherosa Ziehau 	if (nchan == 1) {
657715516c77SSepherosa Ziehau 		/* Only the primary channel can be used; done */
657815516c77SSepherosa Ziehau 		goto back;
657915516c77SSepherosa Ziehau 	}
658015516c77SSepherosa Ziehau 
658115516c77SSepherosa Ziehau 	/*
658271e8ac56SSepherosa Ziehau 	 * Attach the sub-channels.
6583afd4971bSSepherosa Ziehau 	 *
6584afd4971bSSepherosa Ziehau 	 * NOTE: hn_set_ring_inuse() _must_ have been called.
658515516c77SSepherosa Ziehau 	 */
658671e8ac56SSepherosa Ziehau 	error = hn_attach_subchans(sc);
658771e8ac56SSepherosa Ziehau 	if (error)
658871e8ac56SSepherosa Ziehau 		goto failed;
658915516c77SSepherosa Ziehau 
659071e8ac56SSepherosa Ziehau 	/*
659171e8ac56SSepherosa Ziehau 	 * Configure RSS key and indirect table _after_ all sub-channels
659271e8ac56SSepherosa Ziehau 	 * are attached.
659371e8ac56SSepherosa Ziehau 	 */
659415516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSKEY) == 0) {
659515516c77SSepherosa Ziehau 		/*
659615516c77SSepherosa Ziehau 		 * RSS key is not set yet; set it to the default RSS key.
659715516c77SSepherosa Ziehau 		 */
659815516c77SSepherosa Ziehau 		if (bootverbose)
659915516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS key\n");
660034d68912SSepherosa Ziehau #ifdef RSS
660134d68912SSepherosa Ziehau 		rss_getkey(rss->rss_key);
660234d68912SSepherosa Ziehau #else
660315516c77SSepherosa Ziehau 		memcpy(rss->rss_key, hn_rss_key_default, sizeof(rss->rss_key));
660434d68912SSepherosa Ziehau #endif
660515516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
660615516c77SSepherosa Ziehau 	}
660715516c77SSepherosa Ziehau 
660815516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSIND) == 0) {
660915516c77SSepherosa Ziehau 		/*
661015516c77SSepherosa Ziehau 		 * RSS indirect table is not set yet; set it up in round-
661115516c77SSepherosa Ziehau 		 * robin fashion.
661215516c77SSepherosa Ziehau 		 */
661315516c77SSepherosa Ziehau 		if (bootverbose) {
661415516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS indirect "
661515516c77SSepherosa Ziehau 			    "table\n");
661615516c77SSepherosa Ziehau 		}
661734d68912SSepherosa Ziehau 		for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
661834d68912SSepherosa Ziehau 			uint32_t subidx;
661934d68912SSepherosa Ziehau 
662034d68912SSepherosa Ziehau #ifdef RSS
662134d68912SSepherosa Ziehau 			subidx = rss_get_indirection_to_bucket(i);
662234d68912SSepherosa Ziehau #else
662334d68912SSepherosa Ziehau 			subidx = i;
662434d68912SSepherosa Ziehau #endif
662534d68912SSepherosa Ziehau 			rss->rss_ind[i] = subidx % nchan;
662634d68912SSepherosa Ziehau 		}
662715516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSIND;
662815516c77SSepherosa Ziehau 	} else {
662915516c77SSepherosa Ziehau 		/*
663015516c77SSepherosa Ziehau 		 * # of usable channels may be changed, so we have to
663115516c77SSepherosa Ziehau 		 * make sure that all entries in RSS indirect table
663215516c77SSepherosa Ziehau 		 * are valid.
6633afd4971bSSepherosa Ziehau 		 *
6634afd4971bSSepherosa Ziehau 		 * NOTE: hn_set_ring_inuse() _must_ have been called.
663515516c77SSepherosa Ziehau 		 */
6636afd4971bSSepherosa Ziehau 		hn_rss_ind_fixup(sc);
663715516c77SSepherosa Ziehau 	}
663815516c77SSepherosa Ziehau 
6639642ec226SSepherosa Ziehau 	sc->hn_rss_hash = sc->hn_rss_hcap;
6640642ec226SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) ||
6641642ec226SSepherosa Ziehau 	    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
6642642ec226SSepherosa Ziehau 		/* NOTE: Don't reconfigure RSS; will do immediately. */
6643642ec226SSepherosa Ziehau 		hn_vf_rss_fixup(sc, false);
6644642ec226SSepherosa Ziehau 	}
664515516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
664615516c77SSepherosa Ziehau 	if (error)
664771e8ac56SSepherosa Ziehau 		goto failed;
664871e8ac56SSepherosa Ziehau back:
6649dc13fee6SSepherosa Ziehau 	/*
6650dc13fee6SSepherosa Ziehau 	 * Fixup transmission aggregation setup.
6651dc13fee6SSepherosa Ziehau 	 */
6652dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
6653b3b75d9cSSepherosa Ziehau 	hn_rndis_init_fixat(sc, nchan);
665415516c77SSepherosa Ziehau 	return (0);
665571e8ac56SSepherosa Ziehau 
665671e8ac56SSepherosa Ziehau failed:
665771e8ac56SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
6658b3b75d9cSSepherosa Ziehau 		hn_rndis_init_fixat(sc, nchan);
665971e8ac56SSepherosa Ziehau 		hn_synth_detach(sc);
666071e8ac56SSepherosa Ziehau 	} else {
6661b3b75d9cSSepherosa Ziehau 		if (attached & ATTACHED_RNDIS) {
6662b3b75d9cSSepherosa Ziehau 			hn_rndis_init_fixat(sc, nchan);
666371e8ac56SSepherosa Ziehau 			hn_rndis_detach(sc);
6664b3b75d9cSSepherosa Ziehau 		}
666571e8ac56SSepherosa Ziehau 		if (attached & ATTACHED_NVS)
666671e8ac56SSepherosa Ziehau 			hn_nvs_detach(sc);
666771e8ac56SSepherosa Ziehau 		hn_chan_detach(sc, sc->hn_prichan);
666871e8ac56SSepherosa Ziehau 		/* Restore old capabilities. */
666971e8ac56SSepherosa Ziehau 		sc->hn_caps = old_caps;
667071e8ac56SSepherosa Ziehau 	}
667171e8ac56SSepherosa Ziehau 	return (error);
667271e8ac56SSepherosa Ziehau 
667371e8ac56SSepherosa Ziehau #undef ATTACHED_RNDIS
667471e8ac56SSepherosa Ziehau #undef ATTACHED_NVS
667515516c77SSepherosa Ziehau }
667615516c77SSepherosa Ziehau 
667715516c77SSepherosa Ziehau /*
667815516c77SSepherosa Ziehau  * NOTE:
667915516c77SSepherosa Ziehau  * The interface must have been suspended though hn_suspend(), before
668015516c77SSepherosa Ziehau  * this function get called.
668115516c77SSepherosa Ziehau  */
668215516c77SSepherosa Ziehau static void
668315516c77SSepherosa Ziehau hn_synth_detach(struct hn_softc *sc)
668415516c77SSepherosa Ziehau {
668515516c77SSepherosa Ziehau 
668615516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
668715516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
668815516c77SSepherosa Ziehau 
668915516c77SSepherosa Ziehau 	/* Detach the RNDIS first. */
669015516c77SSepherosa Ziehau 	hn_rndis_detach(sc);
669115516c77SSepherosa Ziehau 
669215516c77SSepherosa Ziehau 	/* Detach NVS. */
669315516c77SSepherosa Ziehau 	hn_nvs_detach(sc);
669415516c77SSepherosa Ziehau 
669515516c77SSepherosa Ziehau 	/* Detach all of the channels. */
669615516c77SSepherosa Ziehau 	hn_detach_allchans(sc);
669715516c77SSepherosa Ziehau 
6698ace5ce7eSWei Hu 	if (vmbus_current_version >= VMBUS_VERSION_WIN10 && sc->hn_rxbuf_gpadl != 0) {
6699ace5ce7eSWei Hu 		/*
6700ace5ce7eSWei Hu 		 * Host is post-Win2016, disconnect RXBUF from primary channel here.
6701ace5ce7eSWei Hu 		 */
6702ace5ce7eSWei Hu 		int error;
6703ace5ce7eSWei Hu 
6704ace5ce7eSWei Hu 		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
6705ace5ce7eSWei Hu 		    sc->hn_rxbuf_gpadl);
6706ace5ce7eSWei Hu 		if (error) {
6707ace5ce7eSWei Hu 			if_printf(sc->hn_ifp,
6708ace5ce7eSWei Hu 			    "rxbuf gpadl disconn failed: %d\n", error);
6709ace5ce7eSWei Hu 			sc->hn_flags |= HN_FLAG_RXBUF_REF;
6710ace5ce7eSWei Hu 		}
6711ace5ce7eSWei Hu 		sc->hn_rxbuf_gpadl = 0;
6712ace5ce7eSWei Hu 	}
6713ace5ce7eSWei Hu 
6714ace5ce7eSWei Hu 	if (vmbus_current_version >= VMBUS_VERSION_WIN10 && sc->hn_chim_gpadl != 0) {
6715ace5ce7eSWei Hu 		/*
6716ace5ce7eSWei Hu 		 * Host is post-Win2016, disconnect chimney sending buffer from
6717ace5ce7eSWei Hu 		 * primary channel here.
6718ace5ce7eSWei Hu 		 */
6719ace5ce7eSWei Hu 		int error;
6720ace5ce7eSWei Hu 
6721ace5ce7eSWei Hu 		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
6722ace5ce7eSWei Hu 		    sc->hn_chim_gpadl);
6723ace5ce7eSWei Hu 		if (error) {
6724ace5ce7eSWei Hu 			if_printf(sc->hn_ifp,
6725ace5ce7eSWei Hu 			    "chim gpadl disconn failed: %d\n", error);
6726ace5ce7eSWei Hu 			sc->hn_flags |= HN_FLAG_CHIM_REF;
6727ace5ce7eSWei Hu 		}
6728ace5ce7eSWei Hu 		sc->hn_chim_gpadl = 0;
6729ace5ce7eSWei Hu 	}
673015516c77SSepherosa Ziehau 	sc->hn_flags &= ~HN_FLAG_SYNTH_ATTACHED;
673115516c77SSepherosa Ziehau }
673215516c77SSepherosa Ziehau 
673315516c77SSepherosa Ziehau static void
673415516c77SSepherosa Ziehau hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt)
673515516c77SSepherosa Ziehau {
673615516c77SSepherosa Ziehau 	KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_cnt,
673715516c77SSepherosa Ziehau 	    ("invalid ring count %d", ring_cnt));
673815516c77SSepherosa Ziehau 
673915516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt > ring_cnt)
674015516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = ring_cnt;
674115516c77SSepherosa Ziehau 	else
674215516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
674315516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = ring_cnt;
674415516c77SSepherosa Ziehau 
674534d68912SSepherosa Ziehau #ifdef RSS
674634d68912SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse != rss_getnumbuckets()) {
674734d68912SSepherosa Ziehau 		if_printf(sc->hn_ifp, "# of RX rings (%d) does not match "
674834d68912SSepherosa Ziehau 		    "# of RSS buckets (%d)\n", sc->hn_rx_ring_inuse,
674934d68912SSepherosa Ziehau 		    rss_getnumbuckets());
675034d68912SSepherosa Ziehau 	}
675134d68912SSepherosa Ziehau #endif
675234d68912SSepherosa Ziehau 
675315516c77SSepherosa Ziehau 	if (bootverbose) {
675415516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n",
675515516c77SSepherosa Ziehau 		    sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
675615516c77SSepherosa Ziehau 	}
675715516c77SSepherosa Ziehau }
675815516c77SSepherosa Ziehau 
675915516c77SSepherosa Ziehau static void
676025641fc7SSepherosa Ziehau hn_chan_drain(struct hn_softc *sc, struct vmbus_channel *chan)
676115516c77SSepherosa Ziehau {
676215516c77SSepherosa Ziehau 
676325641fc7SSepherosa Ziehau 	/*
676425641fc7SSepherosa Ziehau 	 * NOTE:
676525641fc7SSepherosa Ziehau 	 * The TX bufring will not be drained by the hypervisor,
676625641fc7SSepherosa Ziehau 	 * if the primary channel is revoked.
676725641fc7SSepherosa Ziehau 	 */
676825641fc7SSepherosa Ziehau 	while (!vmbus_chan_rx_empty(chan) ||
676925641fc7SSepherosa Ziehau 	    (!vmbus_chan_is_revoked(sc->hn_prichan) &&
677025641fc7SSepherosa Ziehau 	     !vmbus_chan_tx_empty(chan)))
677115516c77SSepherosa Ziehau 		pause("waitch", 1);
677215516c77SSepherosa Ziehau 	vmbus_chan_intr_drain(chan);
677315516c77SSepherosa Ziehau }
677415516c77SSepherosa Ziehau 
677515516c77SSepherosa Ziehau static void
6776b3b75d9cSSepherosa Ziehau hn_disable_rx(struct hn_softc *sc)
6777b3b75d9cSSepherosa Ziehau {
6778b3b75d9cSSepherosa Ziehau 
6779b3b75d9cSSepherosa Ziehau 	/*
6780b3b75d9cSSepherosa Ziehau 	 * Disable RX by clearing RX filter forcefully.
6781b3b75d9cSSepherosa Ziehau 	 */
6782b3b75d9cSSepherosa Ziehau 	sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE;
6783b3b75d9cSSepherosa Ziehau 	hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); /* ignore error */
6784b3b75d9cSSepherosa Ziehau 
6785b3b75d9cSSepherosa Ziehau 	/*
6786b3b75d9cSSepherosa Ziehau 	 * Give RNDIS enough time to flush all pending data packets.
6787b3b75d9cSSepherosa Ziehau 	 */
6788b3b75d9cSSepherosa Ziehau 	pause("waitrx", (200 * hz) / 1000);
6789b3b75d9cSSepherosa Ziehau }
6790b3b75d9cSSepherosa Ziehau 
6791b3b75d9cSSepherosa Ziehau /*
6792b3b75d9cSSepherosa Ziehau  * NOTE:
6793b3b75d9cSSepherosa Ziehau  * RX/TX _must_ have been suspended/disabled, before this function
6794b3b75d9cSSepherosa Ziehau  * is called.
6795b3b75d9cSSepherosa Ziehau  */
6796b3b75d9cSSepherosa Ziehau static void
6797b3b75d9cSSepherosa Ziehau hn_drain_rxtx(struct hn_softc *sc, int nchan)
679815516c77SSepherosa Ziehau {
679915516c77SSepherosa Ziehau 	struct vmbus_channel **subch = NULL;
6800b3b75d9cSSepherosa Ziehau 	int nsubch;
6801b3b75d9cSSepherosa Ziehau 
6802b3b75d9cSSepherosa Ziehau 	/*
6803b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX bufrings and interrupts.
6804b3b75d9cSSepherosa Ziehau 	 */
6805b3b75d9cSSepherosa Ziehau 	nsubch = nchan - 1;
6806b3b75d9cSSepherosa Ziehau 	if (nsubch > 0)
6807b3b75d9cSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
6808b3b75d9cSSepherosa Ziehau 
6809b3b75d9cSSepherosa Ziehau 	if (subch != NULL) {
6810b3b75d9cSSepherosa Ziehau 		int i;
6811b3b75d9cSSepherosa Ziehau 
6812b3b75d9cSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
6813b3b75d9cSSepherosa Ziehau 			hn_chan_drain(sc, subch[i]);
6814b3b75d9cSSepherosa Ziehau 	}
6815b3b75d9cSSepherosa Ziehau 	hn_chan_drain(sc, sc->hn_prichan);
6816b3b75d9cSSepherosa Ziehau 
6817b3b75d9cSSepherosa Ziehau 	if (subch != NULL)
6818b3b75d9cSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
6819b3b75d9cSSepherosa Ziehau }
6820b3b75d9cSSepherosa Ziehau 
6821b3b75d9cSSepherosa Ziehau static void
6822b3b75d9cSSepherosa Ziehau hn_suspend_data(struct hn_softc *sc)
6823b3b75d9cSSepherosa Ziehau {
682425641fc7SSepherosa Ziehau 	struct hn_tx_ring *txr;
6825b3b75d9cSSepherosa Ziehau 	int i;
682615516c77SSepherosa Ziehau 
682715516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
682815516c77SSepherosa Ziehau 
682915516c77SSepherosa Ziehau 	/*
683015516c77SSepherosa Ziehau 	 * Suspend TX.
683115516c77SSepherosa Ziehau 	 */
683215516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
683325641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
683415516c77SSepherosa Ziehau 
683515516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
683615516c77SSepherosa Ziehau 		txr->hn_suspended = 1;
683715516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
683815516c77SSepherosa Ziehau 		/* No one is able send more packets now. */
683915516c77SSepherosa Ziehau 
684025641fc7SSepherosa Ziehau 		/*
684125641fc7SSepherosa Ziehau 		 * Wait for all pending sends to finish.
684225641fc7SSepherosa Ziehau 		 *
684325641fc7SSepherosa Ziehau 		 * NOTE:
684425641fc7SSepherosa Ziehau 		 * We will _not_ receive all pending send-done, if the
684525641fc7SSepherosa Ziehau 		 * primary channel is revoked.
684625641fc7SSepherosa Ziehau 		 */
684725641fc7SSepherosa Ziehau 		while (hn_tx_ring_pending(txr) &&
684825641fc7SSepherosa Ziehau 		    !vmbus_chan_is_revoked(sc->hn_prichan))
684915516c77SSepherosa Ziehau 			pause("hnwtx", 1 /* 1 tick */);
685015516c77SSepherosa Ziehau 	}
685115516c77SSepherosa Ziehau 
685215516c77SSepherosa Ziehau 	/*
6853b3b75d9cSSepherosa Ziehau 	 * Disable RX.
685415516c77SSepherosa Ziehau 	 */
6855b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
685615516c77SSepherosa Ziehau 
685715516c77SSepherosa Ziehau 	/*
6858b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX.
685915516c77SSepherosa Ziehau 	 */
6860b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, sc->hn_rx_ring_inuse);
686125641fc7SSepherosa Ziehau 
686225641fc7SSepherosa Ziehau 	/*
686325641fc7SSepherosa Ziehau 	 * Drain any pending TX tasks.
686425641fc7SSepherosa Ziehau 	 *
686525641fc7SSepherosa Ziehau 	 * NOTE:
6866b3b75d9cSSepherosa Ziehau 	 * The above hn_drain_rxtx() can dispatch TX tasks, so the TX
6867b3b75d9cSSepherosa Ziehau 	 * tasks will have to be drained _after_ the above hn_drain_rxtx().
686825641fc7SSepherosa Ziehau 	 */
686925641fc7SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
687025641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
687125641fc7SSepherosa Ziehau 
687225641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task);
687325641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task);
687425641fc7SSepherosa Ziehau 	}
687515516c77SSepherosa Ziehau }
687615516c77SSepherosa Ziehau 
687715516c77SSepherosa Ziehau static void
687815516c77SSepherosa Ziehau hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused)
687915516c77SSepherosa Ziehau {
688015516c77SSepherosa Ziehau 
688115516c77SSepherosa Ziehau 	((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL;
688215516c77SSepherosa Ziehau }
688315516c77SSepherosa Ziehau 
688415516c77SSepherosa Ziehau static void
688515516c77SSepherosa Ziehau hn_suspend_mgmt(struct hn_softc *sc)
688615516c77SSepherosa Ziehau {
688715516c77SSepherosa Ziehau 	struct task task;
688815516c77SSepherosa Ziehau 
688915516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
689015516c77SSepherosa Ziehau 
689115516c77SSepherosa Ziehau 	/*
689215516c77SSepherosa Ziehau 	 * Make sure that hn_mgmt_taskq0 can nolonger be accessed
689315516c77SSepherosa Ziehau 	 * through hn_mgmt_taskq.
689415516c77SSepherosa Ziehau 	 */
689515516c77SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_suspend_mgmt_taskfunc, sc);
689615516c77SSepherosa Ziehau 	vmbus_chan_run_task(sc->hn_prichan, &task);
689715516c77SSepherosa Ziehau 
689815516c77SSepherosa Ziehau 	/*
689915516c77SSepherosa Ziehau 	 * Make sure that all pending management tasks are completed.
690015516c77SSepherosa Ziehau 	 */
690115516c77SSepherosa Ziehau 	taskqueue_drain(sc->hn_mgmt_taskq0, &sc->hn_netchg_init);
690215516c77SSepherosa Ziehau 	taskqueue_drain_timeout(sc->hn_mgmt_taskq0, &sc->hn_netchg_status);
690315516c77SSepherosa Ziehau 	taskqueue_drain_all(sc->hn_mgmt_taskq0);
690415516c77SSepherosa Ziehau }
690515516c77SSepherosa Ziehau 
690615516c77SSepherosa Ziehau static void
690715516c77SSepherosa Ziehau hn_suspend(struct hn_softc *sc)
690815516c77SSepherosa Ziehau {
690915516c77SSepherosa Ziehau 
691087f8129dSSepherosa Ziehau 	/* Disable polling. */
691187f8129dSSepherosa Ziehau 	hn_polling(sc, 0);
691287f8129dSSepherosa Ziehau 
69139c6cae24SSepherosa Ziehau 	/*
69149c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
69159c6cae24SSepherosa Ziehau 	 * device is receiving packets, so the data path of the
69169c6cae24SSepherosa Ziehau 	 * synthetic device must be suspended.
69179c6cae24SSepherosa Ziehau 	 */
69184db5958aSJustin Hibbits 	if ((if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) ||
6919962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
692015516c77SSepherosa Ziehau 		hn_suspend_data(sc);
692115516c77SSepherosa Ziehau 	hn_suspend_mgmt(sc);
692215516c77SSepherosa Ziehau }
692315516c77SSepherosa Ziehau 
692415516c77SSepherosa Ziehau static void
692515516c77SSepherosa Ziehau hn_resume_tx(struct hn_softc *sc, int tx_ring_cnt)
692615516c77SSepherosa Ziehau {
692715516c77SSepherosa Ziehau 	int i;
692815516c77SSepherosa Ziehau 
692915516c77SSepherosa Ziehau 	KASSERT(tx_ring_cnt <= sc->hn_tx_ring_cnt,
693015516c77SSepherosa Ziehau 	    ("invalid TX ring count %d", tx_ring_cnt));
693115516c77SSepherosa Ziehau 
693215516c77SSepherosa Ziehau 	for (i = 0; i < tx_ring_cnt; ++i) {
693315516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
693415516c77SSepherosa Ziehau 
693515516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
693615516c77SSepherosa Ziehau 		txr->hn_suspended = 0;
693715516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
693815516c77SSepherosa Ziehau 	}
693915516c77SSepherosa Ziehau }
694015516c77SSepherosa Ziehau 
694115516c77SSepherosa Ziehau static void
694215516c77SSepherosa Ziehau hn_resume_data(struct hn_softc *sc)
694315516c77SSepherosa Ziehau {
694415516c77SSepherosa Ziehau 	int i;
694515516c77SSepherosa Ziehau 
694615516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
694715516c77SSepherosa Ziehau 
694815516c77SSepherosa Ziehau 	/*
694915516c77SSepherosa Ziehau 	 * Re-enable RX.
695015516c77SSepherosa Ziehau 	 */
6951c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
695215516c77SSepherosa Ziehau 
695315516c77SSepherosa Ziehau 	/*
695415516c77SSepherosa Ziehau 	 * Make sure to clear suspend status on "all" TX rings,
695515516c77SSepherosa Ziehau 	 * since hn_tx_ring_inuse can be changed after
695615516c77SSepherosa Ziehau 	 * hn_suspend_data().
695715516c77SSepherosa Ziehau 	 */
695815516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_cnt);
695915516c77SSepherosa Ziehau 
696023bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
696123bf9e15SSepherosa Ziehau 	if (!hn_use_if_start)
696223bf9e15SSepherosa Ziehau #endif
696323bf9e15SSepherosa Ziehau 	{
696415516c77SSepherosa Ziehau 		/*
696515516c77SSepherosa Ziehau 		 * Flush unused drbrs, since hn_tx_ring_inuse may be
696615516c77SSepherosa Ziehau 		 * reduced.
696715516c77SSepherosa Ziehau 		 */
696815516c77SSepherosa Ziehau 		for (i = sc->hn_tx_ring_inuse; i < sc->hn_tx_ring_cnt; ++i)
696915516c77SSepherosa Ziehau 			hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
697015516c77SSepherosa Ziehau 	}
697115516c77SSepherosa Ziehau 
697215516c77SSepherosa Ziehau 	/*
697315516c77SSepherosa Ziehau 	 * Kick start TX.
697415516c77SSepherosa Ziehau 	 */
697515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
697615516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
697715516c77SSepherosa Ziehau 
697815516c77SSepherosa Ziehau 		/*
697915516c77SSepherosa Ziehau 		 * Use txeof task, so that any pending oactive can be
698015516c77SSepherosa Ziehau 		 * cleared properly.
698115516c77SSepherosa Ziehau 		 */
698215516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
698315516c77SSepherosa Ziehau 	}
698415516c77SSepherosa Ziehau }
698515516c77SSepherosa Ziehau 
698615516c77SSepherosa Ziehau static void
698715516c77SSepherosa Ziehau hn_resume_mgmt(struct hn_softc *sc)
698815516c77SSepherosa Ziehau {
698915516c77SSepherosa Ziehau 
699015516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
699115516c77SSepherosa Ziehau 
699215516c77SSepherosa Ziehau 	/*
699315516c77SSepherosa Ziehau 	 * Kick off network change detection, if it was pending.
699415516c77SSepherosa Ziehau 	 * If no network change was pending, start link status
699515516c77SSepherosa Ziehau 	 * checks, which is more lightweight than network change
699615516c77SSepherosa Ziehau 	 * detection.
699715516c77SSepherosa Ziehau 	 */
699815516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
699915516c77SSepherosa Ziehau 		hn_change_network(sc);
700015516c77SSepherosa Ziehau 	else
700115516c77SSepherosa Ziehau 		hn_update_link_status(sc);
700215516c77SSepherosa Ziehau }
700315516c77SSepherosa Ziehau 
700415516c77SSepherosa Ziehau static void
700515516c77SSepherosa Ziehau hn_resume(struct hn_softc *sc)
700615516c77SSepherosa Ziehau {
700715516c77SSepherosa Ziehau 
70089c6cae24SSepherosa Ziehau 	/*
70099c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
70109c6cae24SSepherosa Ziehau 	 * device have to receive packets, so the data path of the
70119c6cae24SSepherosa Ziehau 	 * synthetic device must be resumed.
70129c6cae24SSepherosa Ziehau 	 */
70134db5958aSJustin Hibbits 	if ((if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) ||
7014962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
701515516c77SSepherosa Ziehau 		hn_resume_data(sc);
70165bdfd3fdSDexuan Cui 
70175bdfd3fdSDexuan Cui 	/*
70189c6cae24SSepherosa Ziehau 	 * Don't resume link status change if VF is attached/activated.
70199c6cae24SSepherosa Ziehau 	 * - In the non-transparent VF mode, the synthetic device marks
70209c6cae24SSepherosa Ziehau 	 *   link down until the VF is deactivated; i.e. VF is down.
70219c6cae24SSepherosa Ziehau 	 * - In transparent VF mode, VF's media status is used until
70229c6cae24SSepherosa Ziehau 	 *   the VF is detached.
70235bdfd3fdSDexuan Cui 	 */
70249c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) == 0 &&
70259c6cae24SSepherosa Ziehau 	    !(hn_xpnt_vf && sc->hn_vf_ifp != NULL))
702615516c77SSepherosa Ziehau 		hn_resume_mgmt(sc);
702787f8129dSSepherosa Ziehau 
702887f8129dSSepherosa Ziehau 	/*
702987f8129dSSepherosa Ziehau 	 * Re-enable polling if this interface is running and
703087f8129dSSepherosa Ziehau 	 * the polling is requested.
703187f8129dSSepherosa Ziehau 	 */
70324db5958aSJustin Hibbits 	if ((if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) && sc->hn_pollhz > 0)
703387f8129dSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
703415516c77SSepherosa Ziehau }
703515516c77SSepherosa Ziehau 
703615516c77SSepherosa Ziehau static void
703715516c77SSepherosa Ziehau hn_rndis_rx_status(struct hn_softc *sc, const void *data, int dlen)
703815516c77SSepherosa Ziehau {
703915516c77SSepherosa Ziehau 	const struct rndis_status_msg *msg;
704015516c77SSepherosa Ziehau 	int ofs;
704115516c77SSepherosa Ziehau 
704215516c77SSepherosa Ziehau 	if (dlen < sizeof(*msg)) {
704315516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid RNDIS status\n");
704415516c77SSepherosa Ziehau 		return;
704515516c77SSepherosa Ziehau 	}
704615516c77SSepherosa Ziehau 	msg = data;
704715516c77SSepherosa Ziehau 
704815516c77SSepherosa Ziehau 	switch (msg->rm_status) {
704915516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_CONNECT:
705015516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_DISCONNECT:
705115516c77SSepherosa Ziehau 		hn_update_link_status(sc);
705215516c77SSepherosa Ziehau 		break;
705315516c77SSepherosa Ziehau 
705415516c77SSepherosa Ziehau 	case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
705540905afaSSepherosa Ziehau 	case RNDIS_STATUS_LINK_SPEED_CHANGE:
705615516c77SSepherosa Ziehau 		/* Not really useful; ignore. */
705715516c77SSepherosa Ziehau 		break;
705815516c77SSepherosa Ziehau 
705915516c77SSepherosa Ziehau 	case RNDIS_STATUS_NETWORK_CHANGE:
706015516c77SSepherosa Ziehau 		ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
706115516c77SSepherosa Ziehau 		if (dlen < ofs + msg->rm_stbuflen ||
706215516c77SSepherosa Ziehau 		    msg->rm_stbuflen < sizeof(uint32_t)) {
706315516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed\n");
706415516c77SSepherosa Ziehau 		} else {
706515516c77SSepherosa Ziehau 			uint32_t change;
706615516c77SSepherosa Ziehau 
706715516c77SSepherosa Ziehau 			memcpy(&change, ((const uint8_t *)msg) + ofs,
706815516c77SSepherosa Ziehau 			    sizeof(change));
706915516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed, change %u\n",
707015516c77SSepherosa Ziehau 			    change);
707115516c77SSepherosa Ziehau 		}
707215516c77SSepherosa Ziehau 		hn_change_network(sc);
707315516c77SSepherosa Ziehau 		break;
707415516c77SSepherosa Ziehau 
707515516c77SSepherosa Ziehau 	default:
707615516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
707715516c77SSepherosa Ziehau 		    msg->rm_status);
707815516c77SSepherosa Ziehau 		break;
707915516c77SSepherosa Ziehau 	}
708015516c77SSepherosa Ziehau }
708115516c77SSepherosa Ziehau 
708215516c77SSepherosa Ziehau static int
708315516c77SSepherosa Ziehau hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info)
708415516c77SSepherosa Ziehau {
708515516c77SSepherosa Ziehau 	const struct rndis_pktinfo *pi = info_data;
708615516c77SSepherosa Ziehau 	uint32_t mask = 0;
708715516c77SSepherosa Ziehau 
708815516c77SSepherosa Ziehau 	while (info_dlen != 0) {
708915516c77SSepherosa Ziehau 		const void *data;
709015516c77SSepherosa Ziehau 		uint32_t dlen;
709115516c77SSepherosa Ziehau 
709215516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < sizeof(*pi)))
709315516c77SSepherosa Ziehau 			return (EINVAL);
709415516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < pi->rm_size))
709515516c77SSepherosa Ziehau 			return (EINVAL);
709615516c77SSepherosa Ziehau 		info_dlen -= pi->rm_size;
709715516c77SSepherosa Ziehau 
709815516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
709915516c77SSepherosa Ziehau 			return (EINVAL);
710015516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
710115516c77SSepherosa Ziehau 			return (EINVAL);
710215516c77SSepherosa Ziehau 		dlen = pi->rm_size - pi->rm_pktinfooffset;
710315516c77SSepherosa Ziehau 		data = pi->rm_data;
710415516c77SSepherosa Ziehau 
7105a491581fSWei Hu 		if (pi->rm_internal == 1) {
7106a491581fSWei Hu 			switch (pi->rm_type) {
7107a491581fSWei Hu 			case NDIS_PKTINFO_IT_PKTINFO_ID:
7108a491581fSWei Hu 				if (__predict_false(dlen < NDIS_PKTINFOID_SZ))
7109a491581fSWei Hu 					return (EINVAL);
7110a491581fSWei Hu 				info->pktinfo_id =
7111a491581fSWei Hu 				    (const struct packet_info_id *)data;
7112a491581fSWei Hu 				mask |= HN_RXINFO_PKTINFO_ID;
7113a491581fSWei Hu 				break;
7114a491581fSWei Hu 
7115a491581fSWei Hu 			default:
7116a491581fSWei Hu 				goto next;
7117a491581fSWei Hu 			}
7118a491581fSWei Hu 		} else {
711915516c77SSepherosa Ziehau 			switch (pi->rm_type) {
712015516c77SSepherosa Ziehau 			case NDIS_PKTINFO_TYPE_VLAN:
7121a491581fSWei Hu 				if (__predict_false(dlen
7122a491581fSWei Hu 				    < NDIS_VLAN_INFO_SIZE))
712315516c77SSepherosa Ziehau 					return (EINVAL);
7124a491581fSWei Hu 				info->vlan_info = (const uint32_t *)data;
712515516c77SSepherosa Ziehau 				mask |= HN_RXINFO_VLAN;
712615516c77SSepherosa Ziehau 				break;
712715516c77SSepherosa Ziehau 
712815516c77SSepherosa Ziehau 			case NDIS_PKTINFO_TYPE_CSUM:
7129a491581fSWei Hu 				if (__predict_false(dlen
7130a491581fSWei Hu 				    < NDIS_RXCSUM_INFO_SIZE))
713115516c77SSepherosa Ziehau 					return (EINVAL);
7132a491581fSWei Hu 				info->csum_info = (const uint32_t *)data;
713315516c77SSepherosa Ziehau 				mask |= HN_RXINFO_CSUM;
713415516c77SSepherosa Ziehau 				break;
713515516c77SSepherosa Ziehau 
713615516c77SSepherosa Ziehau 			case HN_NDIS_PKTINFO_TYPE_HASHVAL:
7137a491581fSWei Hu 				if (__predict_false(dlen
7138a491581fSWei Hu 				    < HN_NDIS_HASH_VALUE_SIZE))
713915516c77SSepherosa Ziehau 					return (EINVAL);
7140a491581fSWei Hu 				info->hash_value = (const uint32_t *)data;
714115516c77SSepherosa Ziehau 				mask |= HN_RXINFO_HASHVAL;
714215516c77SSepherosa Ziehau 				break;
714315516c77SSepherosa Ziehau 
714415516c77SSepherosa Ziehau 			case HN_NDIS_PKTINFO_TYPE_HASHINF:
7145a491581fSWei Hu 				if (__predict_false(dlen
7146a491581fSWei Hu 				    < HN_NDIS_HASH_INFO_SIZE))
714715516c77SSepherosa Ziehau 					return (EINVAL);
7148a491581fSWei Hu 				info->hash_info = (const uint32_t *)data;
714915516c77SSepherosa Ziehau 				mask |= HN_RXINFO_HASHINF;
715015516c77SSepherosa Ziehau 				break;
715115516c77SSepherosa Ziehau 
715215516c77SSepherosa Ziehau 			default:
715315516c77SSepherosa Ziehau 				goto next;
715415516c77SSepherosa Ziehau 			}
7155a491581fSWei Hu 		}
715615516c77SSepherosa Ziehau 
715715516c77SSepherosa Ziehau 		if (mask == HN_RXINFO_ALL) {
715815516c77SSepherosa Ziehau 			/* All found; done */
715915516c77SSepherosa Ziehau 			break;
716015516c77SSepherosa Ziehau 		}
716115516c77SSepherosa Ziehau next:
716215516c77SSepherosa Ziehau 		pi = (const struct rndis_pktinfo *)
716315516c77SSepherosa Ziehau 		    ((const uint8_t *)pi + pi->rm_size);
716415516c77SSepherosa Ziehau 	}
716515516c77SSepherosa Ziehau 
716615516c77SSepherosa Ziehau 	/*
716715516c77SSepherosa Ziehau 	 * Final fixup.
716815516c77SSepherosa Ziehau 	 * - If there is no hash value, invalidate the hash info.
716915516c77SSepherosa Ziehau 	 */
717015516c77SSepherosa Ziehau 	if ((mask & HN_RXINFO_HASHVAL) == 0)
7171a491581fSWei Hu 		info->hash_info = NULL;
717215516c77SSepherosa Ziehau 	return (0);
717315516c77SSepherosa Ziehau }
717415516c77SSepherosa Ziehau 
717515516c77SSepherosa Ziehau static __inline bool
717615516c77SSepherosa Ziehau hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
717715516c77SSepherosa Ziehau {
717815516c77SSepherosa Ziehau 
717915516c77SSepherosa Ziehau 	if (off < check_off) {
718015516c77SSepherosa Ziehau 		if (__predict_true(off + len <= check_off))
718115516c77SSepherosa Ziehau 			return (false);
718215516c77SSepherosa Ziehau 	} else if (off > check_off) {
718315516c77SSepherosa Ziehau 		if (__predict_true(check_off + check_len <= off))
718415516c77SSepherosa Ziehau 			return (false);
718515516c77SSepherosa Ziehau 	}
718615516c77SSepherosa Ziehau 	return (true);
718715516c77SSepherosa Ziehau }
718815516c77SSepherosa Ziehau 
7189a491581fSWei Hu static __inline void
7190a491581fSWei Hu hn_rsc_add_data(struct hn_rx_ring *rxr, const void *data,
7191a491581fSWei Hu 		uint32_t len, struct hn_rxinfo *info)
7192a491581fSWei Hu {
7193a491581fSWei Hu 	uint32_t cnt = rxr->rsc.cnt;
7194a491581fSWei Hu 
7195a491581fSWei Hu 	if (cnt) {
7196a491581fSWei Hu 		rxr->rsc.pktlen += len;
7197a491581fSWei Hu 	} else {
7198a491581fSWei Hu 		rxr->rsc.vlan_info = info->vlan_info;
7199a491581fSWei Hu 		rxr->rsc.csum_info = info->csum_info;
7200a491581fSWei Hu 		rxr->rsc.hash_info = info->hash_info;
7201a491581fSWei Hu 		rxr->rsc.hash_value = info->hash_value;
7202a491581fSWei Hu 		rxr->rsc.pktlen = len;
7203a491581fSWei Hu 	}
7204a491581fSWei Hu 
7205a491581fSWei Hu 	rxr->rsc.frag_data[cnt] = data;
7206a491581fSWei Hu 	rxr->rsc.frag_len[cnt] = len;
7207a491581fSWei Hu 	rxr->rsc.cnt++;
7208a491581fSWei Hu }
7209a491581fSWei Hu 
721015516c77SSepherosa Ziehau static void
721115516c77SSepherosa Ziehau hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen)
721215516c77SSepherosa Ziehau {
721315516c77SSepherosa Ziehau 	const struct rndis_packet_msg *pkt;
721415516c77SSepherosa Ziehau 	struct hn_rxinfo info;
721515516c77SSepherosa Ziehau 	int data_off, pktinfo_off, data_len, pktinfo_len;
7216a491581fSWei Hu 	bool rsc_more= false;
721715516c77SSepherosa Ziehau 
721815516c77SSepherosa Ziehau 	/*
721915516c77SSepherosa Ziehau 	 * Check length.
722015516c77SSepherosa Ziehau 	 */
722115516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*pkt))) {
722215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
722315516c77SSepherosa Ziehau 		return;
722415516c77SSepherosa Ziehau 	}
722515516c77SSepherosa Ziehau 	pkt = data;
722615516c77SSepherosa Ziehau 
722715516c77SSepherosa Ziehau 	if (__predict_false(dlen < pkt->rm_len)) {
722815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
722915516c77SSepherosa Ziehau 		    "dlen %d, msglen %u\n", dlen, pkt->rm_len);
723015516c77SSepherosa Ziehau 		return;
723115516c77SSepherosa Ziehau 	}
723215516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_len <
723315516c77SSepherosa Ziehau 	    pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
723415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
723515516c77SSepherosa Ziehau 		    "msglen %u, data %u, oob %u, pktinfo %u\n",
723615516c77SSepherosa Ziehau 		    pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
723715516c77SSepherosa Ziehau 		    pkt->rm_pktinfolen);
723815516c77SSepherosa Ziehau 		return;
723915516c77SSepherosa Ziehau 	}
724015516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_datalen == 0)) {
724115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
724215516c77SSepherosa Ziehau 		return;
724315516c77SSepherosa Ziehau 	}
724415516c77SSepherosa Ziehau 
724515516c77SSepherosa Ziehau 	/*
724615516c77SSepherosa Ziehau 	 * Check offests.
724715516c77SSepherosa Ziehau 	 */
724815516c77SSepherosa Ziehau #define IS_OFFSET_INVALID(ofs)			\
724915516c77SSepherosa Ziehau 	((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN ||	\
725015516c77SSepherosa Ziehau 	 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
725115516c77SSepherosa Ziehau 
725215516c77SSepherosa Ziehau 	/* XXX Hyper-V does not meet data offset alignment requirement */
725315516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
725415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
725515516c77SSepherosa Ziehau 		    "data offset %u\n", pkt->rm_dataoffset);
725615516c77SSepherosa Ziehau 		return;
725715516c77SSepherosa Ziehau 	}
725815516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdataoffset > 0 &&
725915516c77SSepherosa Ziehau 	    IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
726015516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
726115516c77SSepherosa Ziehau 		    "oob offset %u\n", pkt->rm_oobdataoffset);
726215516c77SSepherosa Ziehau 		return;
726315516c77SSepherosa Ziehau 	}
726415516c77SSepherosa Ziehau 	if (__predict_true(pkt->rm_pktinfooffset > 0) &&
726515516c77SSepherosa Ziehau 	    __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
726615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
726715516c77SSepherosa Ziehau 		    "pktinfo offset %u\n", pkt->rm_pktinfooffset);
726815516c77SSepherosa Ziehau 		return;
726915516c77SSepherosa Ziehau 	}
727015516c77SSepherosa Ziehau 
727115516c77SSepherosa Ziehau #undef IS_OFFSET_INVALID
727215516c77SSepherosa Ziehau 
727315516c77SSepherosa Ziehau 	data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
727415516c77SSepherosa Ziehau 	data_len = pkt->rm_datalen;
727515516c77SSepherosa Ziehau 	pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
727615516c77SSepherosa Ziehau 	pktinfo_len = pkt->rm_pktinfolen;
727715516c77SSepherosa Ziehau 
727815516c77SSepherosa Ziehau 	/*
727915516c77SSepherosa Ziehau 	 * Check OOB coverage.
728015516c77SSepherosa Ziehau 	 */
728115516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdatalen != 0)) {
728215516c77SSepherosa Ziehau 		int oob_off, oob_len;
728315516c77SSepherosa Ziehau 
728415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "got oobdata\n");
728515516c77SSepherosa Ziehau 		oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
728615516c77SSepherosa Ziehau 		oob_len = pkt->rm_oobdatalen;
728715516c77SSepherosa Ziehau 
728815516c77SSepherosa Ziehau 		if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
728915516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
729015516c77SSepherosa Ziehau 			    "oob overflow, msglen %u, oob abs %d len %d\n",
729115516c77SSepherosa Ziehau 			    pkt->rm_len, oob_off, oob_len);
729215516c77SSepherosa Ziehau 			return;
729315516c77SSepherosa Ziehau 		}
729415516c77SSepherosa Ziehau 
729515516c77SSepherosa Ziehau 		/*
729615516c77SSepherosa Ziehau 		 * Check against data.
729715516c77SSepherosa Ziehau 		 */
729815516c77SSepherosa Ziehau 		if (hn_rndis_check_overlap(oob_off, oob_len,
729915516c77SSepherosa Ziehau 		    data_off, data_len)) {
730015516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
730115516c77SSepherosa Ziehau 			    "oob overlaps data, oob abs %d len %d, "
730215516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
730315516c77SSepherosa Ziehau 			    oob_off, oob_len, data_off, data_len);
730415516c77SSepherosa Ziehau 			return;
730515516c77SSepherosa Ziehau 		}
730615516c77SSepherosa Ziehau 
730715516c77SSepherosa Ziehau 		/*
730815516c77SSepherosa Ziehau 		 * Check against pktinfo.
730915516c77SSepherosa Ziehau 		 */
731015516c77SSepherosa Ziehau 		if (pktinfo_len != 0 &&
731115516c77SSepherosa Ziehau 		    hn_rndis_check_overlap(oob_off, oob_len,
731215516c77SSepherosa Ziehau 		    pktinfo_off, pktinfo_len)) {
731315516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
731415516c77SSepherosa Ziehau 			    "oob overlaps pktinfo, oob abs %d len %d, "
731515516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
731615516c77SSepherosa Ziehau 			    oob_off, oob_len, pktinfo_off, pktinfo_len);
731715516c77SSepherosa Ziehau 			return;
731815516c77SSepherosa Ziehau 		}
731915516c77SSepherosa Ziehau 	}
732015516c77SSepherosa Ziehau 
732115516c77SSepherosa Ziehau 	/*
732215516c77SSepherosa Ziehau 	 * Check per-packet-info coverage and find useful per-packet-info.
732315516c77SSepherosa Ziehau 	 */
7324a491581fSWei Hu 	info.vlan_info = NULL;
7325a491581fSWei Hu 	info.csum_info = NULL;
7326a491581fSWei Hu 	info.hash_info = NULL;
7327a491581fSWei Hu 	info.pktinfo_id = NULL;
7328a491581fSWei Hu 
732915516c77SSepherosa Ziehau 	if (__predict_true(pktinfo_len != 0)) {
733015516c77SSepherosa Ziehau 		bool overlap;
733115516c77SSepherosa Ziehau 		int error;
733215516c77SSepherosa Ziehau 
733315516c77SSepherosa Ziehau 		if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
733415516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
733515516c77SSepherosa Ziehau 			    "pktinfo overflow, msglen %u, "
733615516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
733715516c77SSepherosa Ziehau 			    pkt->rm_len, pktinfo_off, pktinfo_len);
733815516c77SSepherosa Ziehau 			return;
733915516c77SSepherosa Ziehau 		}
734015516c77SSepherosa Ziehau 
734115516c77SSepherosa Ziehau 		/*
734215516c77SSepherosa Ziehau 		 * Check packet info coverage.
734315516c77SSepherosa Ziehau 		 */
734415516c77SSepherosa Ziehau 		overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
734515516c77SSepherosa Ziehau 		    data_off, data_len);
734615516c77SSepherosa Ziehau 		if (__predict_false(overlap)) {
734715516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
734815516c77SSepherosa Ziehau 			    "pktinfo overlap data, pktinfo abs %d len %d, "
734915516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
735015516c77SSepherosa Ziehau 			    pktinfo_off, pktinfo_len, data_off, data_len);
735115516c77SSepherosa Ziehau 			return;
735215516c77SSepherosa Ziehau 		}
735315516c77SSepherosa Ziehau 
735415516c77SSepherosa Ziehau 		/*
735515516c77SSepherosa Ziehau 		 * Find useful per-packet-info.
735615516c77SSepherosa Ziehau 		 */
735715516c77SSepherosa Ziehau 		error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
735815516c77SSepherosa Ziehau 		    pktinfo_len, &info);
735915516c77SSepherosa Ziehau 		if (__predict_false(error)) {
736015516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
736115516c77SSepherosa Ziehau 			    "pktinfo\n");
736215516c77SSepherosa Ziehau 			return;
736315516c77SSepherosa Ziehau 		}
736415516c77SSepherosa Ziehau 	}
736515516c77SSepherosa Ziehau 
736615516c77SSepherosa Ziehau 	if (__predict_false(data_off + data_len > pkt->rm_len)) {
736715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
736815516c77SSepherosa Ziehau 		    "data overflow, msglen %u, data abs %d len %d\n",
736915516c77SSepherosa Ziehau 		    pkt->rm_len, data_off, data_len);
737015516c77SSepherosa Ziehau 		return;
737115516c77SSepherosa Ziehau 	}
7372a491581fSWei Hu 
7373a491581fSWei Hu 	/* Identify RSC fragments, drop invalid packets */
7374a491581fSWei Hu 	if ((info.pktinfo_id != NULL) &&
7375a491581fSWei Hu 	    (info.pktinfo_id->flag & HN_NDIS_PKTINFO_SUBALLOC)) {
7376a491581fSWei Hu 		if (info.pktinfo_id->flag & HN_NDIS_PKTINFO_1ST_FRAG) {
7377a491581fSWei Hu 			rxr->rsc.cnt = 0;
7378a491581fSWei Hu 			rxr->hn_rsc_pkts++;
7379a491581fSWei Hu 		} else if (rxr->rsc.cnt == 0)
7380a491581fSWei Hu 			goto drop;
7381a491581fSWei Hu 
7382a491581fSWei Hu 		rsc_more = true;
7383a491581fSWei Hu 
7384a491581fSWei Hu 		if (info.pktinfo_id->flag & HN_NDIS_PKTINFO_LAST_FRAG)
7385a491581fSWei Hu 			rsc_more = false;
7386a491581fSWei Hu 
7387a491581fSWei Hu 		if (rsc_more && rxr->rsc.is_last)
7388a491581fSWei Hu 			goto drop;
7389a491581fSWei Hu 	} else {
7390a491581fSWei Hu 		rxr->rsc.cnt = 0;
7391a491581fSWei Hu 	}
7392a491581fSWei Hu 
7393a491581fSWei Hu 	if (__predict_false(rxr->rsc.cnt >= HN_NVS_RSC_MAX))
7394a491581fSWei Hu 		goto drop;
7395a491581fSWei Hu 
7396a491581fSWei Hu 	/* Store data in per rx ring structure */
7397a491581fSWei Hu 	hn_rsc_add_data(rxr,((const uint8_t *)pkt) + data_off,
7398a491581fSWei Hu 	    data_len, &info);
7399a491581fSWei Hu 
7400a491581fSWei Hu 	if (rsc_more)
7401a491581fSWei Hu 		return;
7402a491581fSWei Hu 
7403a491581fSWei Hu 	hn_rxpkt(rxr);
7404a491581fSWei Hu 	rxr->rsc.cnt = 0;
7405a491581fSWei Hu 	return;
7406a491581fSWei Hu drop:
7407a491581fSWei Hu 	rxr->hn_rsc_drop++;
7408a491581fSWei Hu 	return;
740915516c77SSepherosa Ziehau }
741015516c77SSepherosa Ziehau 
741115516c77SSepherosa Ziehau static __inline void
741215516c77SSepherosa Ziehau hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen)
741315516c77SSepherosa Ziehau {
741415516c77SSepherosa Ziehau 	const struct rndis_msghdr *hdr;
741515516c77SSepherosa Ziehau 
741615516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*hdr))) {
741715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
741815516c77SSepherosa Ziehau 		return;
741915516c77SSepherosa Ziehau 	}
742015516c77SSepherosa Ziehau 	hdr = data;
742115516c77SSepherosa Ziehau 
742215516c77SSepherosa Ziehau 	if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) {
742315516c77SSepherosa Ziehau 		/* Hot data path. */
742415516c77SSepherosa Ziehau 		hn_rndis_rx_data(rxr, data, dlen);
742515516c77SSepherosa Ziehau 		/* Done! */
742615516c77SSepherosa Ziehau 		return;
742715516c77SSepherosa Ziehau 	}
742815516c77SSepherosa Ziehau 
742915516c77SSepherosa Ziehau 	if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG)
74304db5958aSJustin Hibbits 		hn_rndis_rx_status(if_getsoftc(rxr->hn_ifp), data, dlen);
743115516c77SSepherosa Ziehau 	else
74324db5958aSJustin Hibbits 		hn_rndis_rx_ctrl(if_getsoftc(rxr->hn_ifp), data, dlen);
743315516c77SSepherosa Ziehau }
743415516c77SSepherosa Ziehau 
743515516c77SSepherosa Ziehau static void
743615516c77SSepherosa Ziehau hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
743715516c77SSepherosa Ziehau {
743815516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *hdr;
743915516c77SSepherosa Ziehau 
744015516c77SSepherosa Ziehau 	if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) {
744115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid nvs notify\n");
744215516c77SSepherosa Ziehau 		return;
744315516c77SSepherosa Ziehau 	}
744415516c77SSepherosa Ziehau 	hdr = VMBUS_CHANPKT_CONST_DATA(pkt);
744515516c77SSepherosa Ziehau 
744615516c77SSepherosa Ziehau 	if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) {
744715516c77SSepherosa Ziehau 		/* Useless; ignore */
744815516c77SSepherosa Ziehau 		return;
744915516c77SSepherosa Ziehau 	}
745015516c77SSepherosa Ziehau 	if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type);
745115516c77SSepherosa Ziehau }
745215516c77SSepherosa Ziehau 
745315516c77SSepherosa Ziehau static void
745415516c77SSepherosa Ziehau hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
745515516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkt)
745615516c77SSepherosa Ziehau {
745715516c77SSepherosa Ziehau 	struct hn_nvs_sendctx *sndc;
745815516c77SSepherosa Ziehau 
745915516c77SSepherosa Ziehau 	sndc = (struct hn_nvs_sendctx *)(uintptr_t)pkt->cph_xactid;
746015516c77SSepherosa Ziehau 	sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt),
746115516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_DATALEN(pkt));
746215516c77SSepherosa Ziehau 	/*
746315516c77SSepherosa Ziehau 	 * NOTE:
746415516c77SSepherosa Ziehau 	 * 'sndc' CAN NOT be accessed anymore, since it can be freed by
746515516c77SSepherosa Ziehau 	 * its callback.
746615516c77SSepherosa Ziehau 	 */
746715516c77SSepherosa Ziehau }
746815516c77SSepherosa Ziehau 
746915516c77SSepherosa Ziehau static void
747015516c77SSepherosa Ziehau hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
747115516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkthdr)
747215516c77SSepherosa Ziehau {
747326d79d40SMichael Tuexen 	struct epoch_tracker et;
747415516c77SSepherosa Ziehau 	const struct vmbus_chanpkt_rxbuf *pkt;
747515516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *nvs_hdr;
747615516c77SSepherosa Ziehau 	int count, i, hlen;
747715516c77SSepherosa Ziehau 
747815516c77SSepherosa Ziehau 	if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) {
747915516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n");
748015516c77SSepherosa Ziehau 		return;
748115516c77SSepherosa Ziehau 	}
748215516c77SSepherosa Ziehau 	nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr);
748315516c77SSepherosa Ziehau 
748415516c77SSepherosa Ziehau 	/* Make sure that this is a RNDIS message. */
748515516c77SSepherosa Ziehau 	if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) {
748615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n",
748715516c77SSepherosa Ziehau 		    nvs_hdr->nvs_type);
748815516c77SSepherosa Ziehau 		return;
748915516c77SSepherosa Ziehau 	}
749015516c77SSepherosa Ziehau 
749115516c77SSepherosa Ziehau 	hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen);
749215516c77SSepherosa Ziehau 	if (__predict_false(hlen < sizeof(*pkt))) {
749315516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n");
749415516c77SSepherosa Ziehau 		return;
749515516c77SSepherosa Ziehau 	}
749615516c77SSepherosa Ziehau 	pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr;
749715516c77SSepherosa Ziehau 
749815516c77SSepherosa Ziehau 	if (__predict_false(pkt->cp_rxbuf_id != HN_NVS_RXBUF_SIG)) {
749915516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n",
750015516c77SSepherosa Ziehau 		    pkt->cp_rxbuf_id);
750115516c77SSepherosa Ziehau 		return;
750215516c77SSepherosa Ziehau 	}
750315516c77SSepherosa Ziehau 
750415516c77SSepherosa Ziehau 	count = pkt->cp_rxbuf_cnt;
750515516c77SSepherosa Ziehau 	if (__predict_false(hlen <
750615516c77SSepherosa Ziehau 	    __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) {
750715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count);
750815516c77SSepherosa Ziehau 		return;
750915516c77SSepherosa Ziehau 	}
751015516c77SSepherosa Ziehau 
751126d79d40SMichael Tuexen 	NET_EPOCH_ENTER(et);
751215516c77SSepherosa Ziehau 	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
751315516c77SSepherosa Ziehau 	for (i = 0; i < count; ++i) {
751415516c77SSepherosa Ziehau 		int ofs, len;
751515516c77SSepherosa Ziehau 
751615516c77SSepherosa Ziehau 		ofs = pkt->cp_rxbuf[i].rb_ofs;
751715516c77SSepherosa Ziehau 		len = pkt->cp_rxbuf[i].rb_len;
751815516c77SSepherosa Ziehau 		if (__predict_false(ofs + len > HN_RXBUF_SIZE)) {
751915516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, "
752015516c77SSepherosa Ziehau 			    "ofs %d, len %d\n", i, ofs, len);
752115516c77SSepherosa Ziehau 			continue;
752215516c77SSepherosa Ziehau 		}
7523a491581fSWei Hu 
7524a491581fSWei Hu 		rxr->rsc.is_last = (i == (count - 1));
752515516c77SSepherosa Ziehau 		hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len);
752615516c77SSepherosa Ziehau 	}
752726d79d40SMichael Tuexen 	NET_EPOCH_EXIT(et);
752815516c77SSepherosa Ziehau 
752915516c77SSepherosa Ziehau 	/*
753015516c77SSepherosa Ziehau 	 * Ack the consumed RXBUF associated w/ this channel packet,
753115516c77SSepherosa Ziehau 	 * so that this RXBUF can be recycled by the hypervisor.
753215516c77SSepherosa Ziehau 	 */
753315516c77SSepherosa Ziehau 	hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid);
753415516c77SSepherosa Ziehau }
753515516c77SSepherosa Ziehau 
753615516c77SSepherosa Ziehau static void
753715516c77SSepherosa Ziehau hn_nvs_ack_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
753815516c77SSepherosa Ziehau     uint64_t tid)
753915516c77SSepherosa Ziehau {
754015516c77SSepherosa Ziehau 	struct hn_nvs_rndis_ack ack;
754115516c77SSepherosa Ziehau 	int retries, error;
754215516c77SSepherosa Ziehau 
754315516c77SSepherosa Ziehau 	ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK;
754415516c77SSepherosa Ziehau 	ack.nvs_status = HN_NVS_STATUS_OK;
754515516c77SSepherosa Ziehau 
754615516c77SSepherosa Ziehau 	retries = 0;
754715516c77SSepherosa Ziehau again:
754815516c77SSepherosa Ziehau 	error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
754915516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid);
755015516c77SSepherosa Ziehau 	if (__predict_false(error == EAGAIN)) {
755115516c77SSepherosa Ziehau 		/*
755215516c77SSepherosa Ziehau 		 * NOTE:
755315516c77SSepherosa Ziehau 		 * This should _not_ happen in real world, since the
755415516c77SSepherosa Ziehau 		 * consumption of the TX bufring from the TX path is
755515516c77SSepherosa Ziehau 		 * controlled.
755615516c77SSepherosa Ziehau 		 */
755715516c77SSepherosa Ziehau 		if (rxr->hn_ack_failed == 0)
755815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "RXBUF ack retry\n");
755915516c77SSepherosa Ziehau 		rxr->hn_ack_failed++;
756015516c77SSepherosa Ziehau 		retries++;
756115516c77SSepherosa Ziehau 		if (retries < 10) {
756215516c77SSepherosa Ziehau 			DELAY(100);
756315516c77SSepherosa Ziehau 			goto again;
756415516c77SSepherosa Ziehau 		}
756515516c77SSepherosa Ziehau 		/* RXBUF leaks! */
756615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "RXBUF ack failed\n");
756715516c77SSepherosa Ziehau 	}
756815516c77SSepherosa Ziehau }
756915516c77SSepherosa Ziehau 
757015516c77SSepherosa Ziehau static void
757115516c77SSepherosa Ziehau hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
757215516c77SSepherosa Ziehau {
757315516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr = xrxr;
75744db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(rxr->hn_ifp);
757515516c77SSepherosa Ziehau 
757615516c77SSepherosa Ziehau 	for (;;) {
757715516c77SSepherosa Ziehau 		struct vmbus_chanpkt_hdr *pkt = rxr->hn_pktbuf;
757815516c77SSepherosa Ziehau 		int error, pktlen;
757915516c77SSepherosa Ziehau 
758015516c77SSepherosa Ziehau 		pktlen = rxr->hn_pktbuf_len;
758115516c77SSepherosa Ziehau 		error = vmbus_chan_recv_pkt(chan, pkt, &pktlen);
758215516c77SSepherosa Ziehau 		if (__predict_false(error == ENOBUFS)) {
758315516c77SSepherosa Ziehau 			void *nbuf;
758415516c77SSepherosa Ziehau 			int nlen;
758515516c77SSepherosa Ziehau 
758615516c77SSepherosa Ziehau 			/*
758715516c77SSepherosa Ziehau 			 * Expand channel packet buffer.
758815516c77SSepherosa Ziehau 			 *
758915516c77SSepherosa Ziehau 			 * XXX
759015516c77SSepherosa Ziehau 			 * Use M_WAITOK here, since allocation failure
759115516c77SSepherosa Ziehau 			 * is fatal.
759215516c77SSepherosa Ziehau 			 */
759315516c77SSepherosa Ziehau 			nlen = rxr->hn_pktbuf_len * 2;
759415516c77SSepherosa Ziehau 			while (nlen < pktlen)
759515516c77SSepherosa Ziehau 				nlen *= 2;
759615516c77SSepherosa Ziehau 			nbuf = malloc(nlen, M_DEVBUF, M_WAITOK);
759715516c77SSepherosa Ziehau 
759815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "expand pktbuf %d -> %d\n",
759915516c77SSepherosa Ziehau 			    rxr->hn_pktbuf_len, nlen);
760015516c77SSepherosa Ziehau 
760115516c77SSepherosa Ziehau 			free(rxr->hn_pktbuf, M_DEVBUF);
760215516c77SSepherosa Ziehau 			rxr->hn_pktbuf = nbuf;
760315516c77SSepherosa Ziehau 			rxr->hn_pktbuf_len = nlen;
760415516c77SSepherosa Ziehau 			/* Retry! */
760515516c77SSepherosa Ziehau 			continue;
760615516c77SSepherosa Ziehau 		} else if (__predict_false(error == EAGAIN)) {
760715516c77SSepherosa Ziehau 			/* No more channel packets; done! */
760815516c77SSepherosa Ziehau 			break;
760915516c77SSepherosa Ziehau 		}
761015516c77SSepherosa Ziehau 		KASSERT(!error, ("vmbus_chan_recv_pkt failed: %d", error));
761115516c77SSepherosa Ziehau 
761215516c77SSepherosa Ziehau 		switch (pkt->cph_type) {
761315516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_COMP:
761415516c77SSepherosa Ziehau 			hn_nvs_handle_comp(sc, chan, pkt);
761515516c77SSepherosa Ziehau 			break;
761615516c77SSepherosa Ziehau 
761715516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_RXBUF:
761815516c77SSepherosa Ziehau 			hn_nvs_handle_rxbuf(rxr, chan, pkt);
761915516c77SSepherosa Ziehau 			break;
762015516c77SSepherosa Ziehau 
762115516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_INBAND:
762215516c77SSepherosa Ziehau 			hn_nvs_handle_notify(sc, pkt);
762315516c77SSepherosa Ziehau 			break;
762415516c77SSepherosa Ziehau 
762515516c77SSepherosa Ziehau 		default:
762615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "unknown chan pkt %u\n",
762715516c77SSepherosa Ziehau 			    pkt->cph_type);
762815516c77SSepherosa Ziehau 			break;
762915516c77SSepherosa Ziehau 		}
763015516c77SSepherosa Ziehau 	}
763115516c77SSepherosa Ziehau 	hn_chan_rollup(rxr, rxr->hn_txr);
763215516c77SSepherosa Ziehau }
763315516c77SSepherosa Ziehau 
763415516c77SSepherosa Ziehau static void
7635499c3e17SSepherosa Ziehau hn_sysinit(void *arg __unused)
763615516c77SSepherosa Ziehau {
7637fdd0222aSSepherosa Ziehau 	int i;
7638fdd0222aSSepherosa Ziehau 
76392be266caSSepherosa Ziehau 	hn_udpcs_fixup = counter_u64_alloc(M_WAITOK);
76402be266caSSepherosa Ziehau 
76419c6cae24SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
76429c6cae24SSepherosa Ziehau 	/*
76439c6cae24SSepherosa Ziehau 	 * Don't use ifnet.if_start if transparent VF mode is requested;
76449c6cae24SSepherosa Ziehau 	 * mainly due to the IFF_DRV_OACTIVE flag.
76459c6cae24SSepherosa Ziehau 	 */
76469c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_use_if_start) {
76479c6cae24SSepherosa Ziehau 		hn_use_if_start = 0;
76489c6cae24SSepherosa Ziehau 		printf("hn: tranparent VF mode, if_transmit will be used, "
76499c6cae24SSepherosa Ziehau 		    "instead of if_start\n");
76509c6cae24SSepherosa Ziehau 	}
76519c6cae24SSepherosa Ziehau #endif
76529c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_attwait < HN_XPNT_VF_ATTWAIT_MIN) {
76539c6cae24SSepherosa Ziehau 		printf("hn: invalid transparent VF attach routing "
76549c6cae24SSepherosa Ziehau 		    "wait timeout %d, reset to %d\n",
76559c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_attwait, HN_XPNT_VF_ATTWAIT_MIN);
76569c6cae24SSepherosa Ziehau 		hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
76579c6cae24SSepherosa Ziehau 	}
76589c6cae24SSepherosa Ziehau 
7659fdd0222aSSepherosa Ziehau 	/*
7660499c3e17SSepherosa Ziehau 	 * Initialize VF map.
7661499c3e17SSepherosa Ziehau 	 */
7662499c3e17SSepherosa Ziehau 	rm_init_flags(&hn_vfmap_lock, "hn_vfmap", RM_SLEEPABLE);
7663499c3e17SSepherosa Ziehau 	hn_vfmap_size = HN_VFMAP_SIZE_DEF;
76644db5958aSJustin Hibbits 	hn_vfmap = malloc(sizeof(if_t) * hn_vfmap_size, M_DEVBUF,
7665499c3e17SSepherosa Ziehau 	    M_WAITOK | M_ZERO);
7666499c3e17SSepherosa Ziehau 
7667499c3e17SSepherosa Ziehau 	/*
7668fdd0222aSSepherosa Ziehau 	 * Fix the # of TX taskqueues.
7669fdd0222aSSepherosa Ziehau 	 */
7670fdd0222aSSepherosa Ziehau 	if (hn_tx_taskq_cnt <= 0)
7671fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = 1;
7672fdd0222aSSepherosa Ziehau 	else if (hn_tx_taskq_cnt > mp_ncpus)
7673fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = mp_ncpus;
767415516c77SSepherosa Ziehau 
76750e11868dSSepherosa Ziehau 	/*
76760e11868dSSepherosa Ziehau 	 * Fix the TX taskqueue mode.
76770e11868dSSepherosa Ziehau 	 */
76780e11868dSSepherosa Ziehau 	switch (hn_tx_taskq_mode) {
76790e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_INDEP:
76800e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_GLOBAL:
76810e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_EVTTQ:
76820e11868dSSepherosa Ziehau 		break;
76830e11868dSSepherosa Ziehau 	default:
76840e11868dSSepherosa Ziehau 		hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
76850e11868dSSepherosa Ziehau 		break;
76860e11868dSSepherosa Ziehau 	}
76870e11868dSSepherosa Ziehau 
768815516c77SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV)
768915516c77SSepherosa Ziehau 		return;
769015516c77SSepherosa Ziehau 
76910e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode != HN_TX_TASKQ_M_GLOBAL)
769215516c77SSepherosa Ziehau 		return;
769315516c77SSepherosa Ziehau 
7694fdd0222aSSepherosa Ziehau 	hn_tx_taskque = malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
7695fdd0222aSSepherosa Ziehau 	    M_DEVBUF, M_WAITOK);
7696fdd0222aSSepherosa Ziehau 	for (i = 0; i < hn_tx_taskq_cnt; ++i) {
7697fdd0222aSSepherosa Ziehau 		hn_tx_taskque[i] = taskqueue_create("hn_tx", M_WAITOK,
7698fdd0222aSSepherosa Ziehau 		    taskqueue_thread_enqueue, &hn_tx_taskque[i]);
7699fdd0222aSSepherosa Ziehau 		taskqueue_start_threads(&hn_tx_taskque[i], 1, PI_NET,
7700fdd0222aSSepherosa Ziehau 		    "hn tx%d", i);
7701fdd0222aSSepherosa Ziehau 	}
770215516c77SSepherosa Ziehau }
7703499c3e17SSepherosa Ziehau SYSINIT(hn_sysinit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysinit, NULL);
770415516c77SSepherosa Ziehau 
770515516c77SSepherosa Ziehau static void
7706499c3e17SSepherosa Ziehau hn_sysuninit(void *arg __unused)
770715516c77SSepherosa Ziehau {
770815516c77SSepherosa Ziehau 
7709fdd0222aSSepherosa Ziehau 	if (hn_tx_taskque != NULL) {
7710fdd0222aSSepherosa Ziehau 		int i;
7711fdd0222aSSepherosa Ziehau 
7712fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
7713fdd0222aSSepherosa Ziehau 			taskqueue_free(hn_tx_taskque[i]);
7714fdd0222aSSepherosa Ziehau 		free(hn_tx_taskque, M_DEVBUF);
7715fdd0222aSSepherosa Ziehau 	}
7716499c3e17SSepherosa Ziehau 
7717499c3e17SSepherosa Ziehau 	if (hn_vfmap != NULL)
7718499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
7719499c3e17SSepherosa Ziehau 	rm_destroy(&hn_vfmap_lock);
77202be266caSSepherosa Ziehau 
77212be266caSSepherosa Ziehau 	counter_u64_free(hn_udpcs_fixup);
772215516c77SSepherosa Ziehau }
7723499c3e17SSepherosa Ziehau SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL);
7724