xref: /freebsd/sys/dev/hyperv/netvsc/if_hn.c (revision 4db5958a0691755dbf232801d2f58f4bec0aeeb5)
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 
8815516c77SSepherosa Ziehau #include <machine/atomic.h>
8915516c77SSepherosa Ziehau #include <machine/in_cksum.h>
9015516c77SSepherosa Ziehau 
9115516c77SSepherosa Ziehau #include <net/bpf.h>
9215516c77SSepherosa Ziehau #include <net/ethernet.h>
9315516c77SSepherosa Ziehau #include <net/if.h>
945bdfd3fdSDexuan Cui #include <net/if_dl.h>
9515516c77SSepherosa Ziehau #include <net/if_media.h>
9615516c77SSepherosa Ziehau #include <net/if_types.h>
9715516c77SSepherosa Ziehau #include <net/if_var.h>
9815516c77SSepherosa Ziehau #include <net/rndis.h>
9934d68912SSepherosa Ziehau #ifdef RSS
10034d68912SSepherosa Ziehau #include <net/rss_config.h>
10134d68912SSepherosa Ziehau #endif
10215516c77SSepherosa Ziehau 
10315516c77SSepherosa Ziehau #include <netinet/in_systm.h>
10415516c77SSepherosa Ziehau #include <netinet/in.h>
10515516c77SSepherosa Ziehau #include <netinet/ip.h>
10615516c77SSepherosa Ziehau #include <netinet/ip6.h>
10715516c77SSepherosa Ziehau #include <netinet/tcp.h>
10815516c77SSepherosa Ziehau #include <netinet/tcp_lro.h>
10915516c77SSepherosa Ziehau #include <netinet/udp.h>
11015516c77SSepherosa Ziehau 
11115516c77SSepherosa Ziehau #include <dev/hyperv/include/hyperv.h>
11215516c77SSepherosa Ziehau #include <dev/hyperv/include/hyperv_busdma.h>
11315516c77SSepherosa Ziehau #include <dev/hyperv/include/vmbus.h>
11415516c77SSepherosa Ziehau #include <dev/hyperv/include/vmbus_xact.h>
11515516c77SSepherosa Ziehau 
11615516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/ndis.h>
11715516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/if_hnreg.h>
11815516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/if_hnvar.h>
11915516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/hn_nvs.h>
12015516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/hn_rndis.h>
12115516c77SSepherosa Ziehau 
12215516c77SSepherosa Ziehau #include "vmbus_if.h"
12315516c77SSepherosa Ziehau 
12423bf9e15SSepherosa Ziehau #define HN_IFSTART_SUPPORT
12523bf9e15SSepherosa Ziehau 
12615516c77SSepherosa Ziehau #define HN_RING_CNT_DEF_MAX		8
12715516c77SSepherosa Ziehau 
128499c3e17SSepherosa Ziehau #define HN_VFMAP_SIZE_DEF		8
129499c3e17SSepherosa Ziehau 
1309c6cae24SSepherosa Ziehau #define HN_XPNT_VF_ATTWAIT_MIN		2	/* seconds */
1319c6cae24SSepherosa Ziehau 
13215516c77SSepherosa Ziehau /* YYY should get it from the underlying channel */
13315516c77SSepherosa Ziehau #define HN_TX_DESC_CNT			512
13415516c77SSepherosa Ziehau 
13515516c77SSepherosa Ziehau #define HN_RNDIS_PKT_LEN					\
13615516c77SSepherosa Ziehau 	(sizeof(struct rndis_packet_msg) +			\
13715516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(HN_NDIS_HASH_VALUE_SIZE) +	\
13815516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_VLAN_INFO_SIZE) +		\
13915516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_LSO2_INFO_SIZE) +		\
14015516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_TXCSUM_INFO_SIZE))
14115516c77SSepherosa Ziehau #define HN_RNDIS_PKT_BOUNDARY		PAGE_SIZE
14215516c77SSepherosa Ziehau #define HN_RNDIS_PKT_ALIGN		CACHE_LINE_SIZE
14315516c77SSepherosa Ziehau 
14415516c77SSepherosa Ziehau #define HN_TX_DATA_BOUNDARY		PAGE_SIZE
14515516c77SSepherosa Ziehau #define HN_TX_DATA_MAXSIZE		IP_MAXPACKET
14615516c77SSepherosa Ziehau #define HN_TX_DATA_SEGSIZE		PAGE_SIZE
14715516c77SSepherosa Ziehau /* -1 for RNDIS packet message */
14815516c77SSepherosa Ziehau #define HN_TX_DATA_SEGCNT_MAX		(HN_GPACNT_MAX - 1)
14915516c77SSepherosa Ziehau 
15015516c77SSepherosa Ziehau #define HN_DIRECT_TX_SIZE_DEF		128
15115516c77SSepherosa Ziehau 
15215516c77SSepherosa Ziehau #define HN_EARLY_TXEOF_THRESH		8
15315516c77SSepherosa Ziehau 
15415516c77SSepherosa Ziehau #define HN_PKTBUF_LEN_DEF		(16 * 1024)
15515516c77SSepherosa Ziehau 
15615516c77SSepherosa Ziehau #define HN_LROENT_CNT_DEF		128
15715516c77SSepherosa Ziehau 
15815516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MULTIRX_DEF	(12 * ETHERMTU)
15915516c77SSepherosa Ziehau #define HN_LRO_LENLIM_DEF		(25 * ETHERMTU)
16015516c77SSepherosa Ziehau /* YYY 2*MTU is a bit rough, but should be good enough. */
161*4db5958aSJustin Hibbits #define HN_LRO_LENLIM_MIN(ifp)		(2 * if_getmtu(ifp))
16215516c77SSepherosa Ziehau 
16315516c77SSepherosa Ziehau #define HN_LRO_ACKCNT_DEF		1
16415516c77SSepherosa Ziehau 
16515516c77SSepherosa Ziehau #define HN_LOCK_INIT(sc)		\
16615516c77SSepherosa Ziehau 	sx_init(&(sc)->hn_lock, device_get_nameunit((sc)->hn_dev))
16715516c77SSepherosa Ziehau #define HN_LOCK_DESTROY(sc)		sx_destroy(&(sc)->hn_lock)
16815516c77SSepherosa Ziehau #define HN_LOCK_ASSERT(sc)		sx_assert(&(sc)->hn_lock, SA_XLOCKED)
169fdc4f478SSepherosa Ziehau #define HN_LOCK(sc)					\
170fdc4f478SSepherosa Ziehau do {							\
171b3460f44SWei Hu 	while (sx_try_xlock(&(sc)->hn_lock) == 0) {	\
172b3460f44SWei Hu 		/* Relinquish cpu to avoid deadlock */	\
173b3460f44SWei Hu 		sched_relinquish(curthread);		\
174fdc4f478SSepherosa Ziehau 		DELAY(1000);				\
175b3460f44SWei Hu 	}						\
176fdc4f478SSepherosa Ziehau } while (0)
17715516c77SSepherosa Ziehau #define HN_UNLOCK(sc)			sx_xunlock(&(sc)->hn_lock)
17815516c77SSepherosa Ziehau 
17915516c77SSepherosa Ziehau #define HN_CSUM_IP_MASK			(CSUM_IP | CSUM_IP_TCP | CSUM_IP_UDP)
18015516c77SSepherosa Ziehau #define HN_CSUM_IP6_MASK		(CSUM_IP6_TCP | CSUM_IP6_UDP)
18115516c77SSepherosa Ziehau #define HN_CSUM_IP_HWASSIST(sc)		\
18215516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP_MASK)
18315516c77SSepherosa Ziehau #define HN_CSUM_IP6_HWASSIST(sc)	\
18415516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP6_MASK)
18515516c77SSepherosa Ziehau 
186dc13fee6SSepherosa Ziehau #define HN_PKTSIZE_MIN(align)		\
187dc13fee6SSepherosa Ziehau 	roundup2(ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN - ETHER_CRC_LEN + \
188dc13fee6SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN, (align))
189dc13fee6SSepherosa Ziehau #define HN_PKTSIZE(m, align)		\
190dc13fee6SSepherosa Ziehau 	roundup2((m)->m_pkthdr.len + HN_RNDIS_PKT_LEN, (align))
191dc13fee6SSepherosa Ziehau 
19234d68912SSepherosa Ziehau #ifdef RSS
19334d68912SSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	rss_getcpu((idx) % rss_getnumbuckets())
19434d68912SSepherosa Ziehau #else
1950e11868dSSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	(((sc)->hn_cpu + (idx)) % mp_ncpus)
19634d68912SSepherosa Ziehau #endif
1970e11868dSSepherosa Ziehau 
19815516c77SSepherosa Ziehau struct hn_txdesc {
19915516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
20015516c77SSepherosa Ziehau 	SLIST_ENTRY(hn_txdesc)		link;
20115516c77SSepherosa Ziehau #endif
202dc13fee6SSepherosa Ziehau 	STAILQ_ENTRY(hn_txdesc)		agg_link;
203dc13fee6SSepherosa Ziehau 
204dc13fee6SSepherosa Ziehau 	/* Aggregated txdescs, in sending order. */
205dc13fee6SSepherosa Ziehau 	STAILQ_HEAD(, hn_txdesc)	agg_list;
206dc13fee6SSepherosa Ziehau 
207dc13fee6SSepherosa Ziehau 	/* The oldest packet, if transmission aggregation happens. */
20815516c77SSepherosa Ziehau 	struct mbuf			*m;
20915516c77SSepherosa Ziehau 	struct hn_tx_ring		*txr;
21015516c77SSepherosa Ziehau 	int				refs;
21115516c77SSepherosa Ziehau 	uint32_t			flags;	/* HN_TXD_FLAG_ */
21215516c77SSepherosa Ziehau 	struct hn_nvs_sendctx		send_ctx;
21315516c77SSepherosa Ziehau 	uint32_t			chim_index;
21415516c77SSepherosa Ziehau 	int				chim_size;
21515516c77SSepherosa Ziehau 
21615516c77SSepherosa Ziehau 	bus_dmamap_t			data_dmap;
21715516c77SSepherosa Ziehau 
21815516c77SSepherosa Ziehau 	bus_addr_t			rndis_pkt_paddr;
21915516c77SSepherosa Ziehau 	struct rndis_packet_msg		*rndis_pkt;
22015516c77SSepherosa Ziehau 	bus_dmamap_t			rndis_pkt_dmap;
22115516c77SSepherosa Ziehau };
22215516c77SSepherosa Ziehau 
22315516c77SSepherosa Ziehau #define HN_TXD_FLAG_ONLIST		0x0001
22415516c77SSepherosa Ziehau #define HN_TXD_FLAG_DMAMAP		0x0002
225dc13fee6SSepherosa Ziehau #define HN_TXD_FLAG_ONAGG		0x0004
22615516c77SSepherosa Ziehau 
227a491581fSWei Hu #define	HN_NDIS_PKTINFO_SUBALLOC	0x01
228a491581fSWei Hu #define	HN_NDIS_PKTINFO_1ST_FRAG	0x02
229a491581fSWei Hu #define	HN_NDIS_PKTINFO_LAST_FRAG	0x04
230a491581fSWei Hu 
231a491581fSWei Hu struct packet_info_id {
232a491581fSWei Hu 	uint8_t				ver;
233a491581fSWei Hu 	uint8_t				flag;
234a491581fSWei Hu 	uint16_t			pkt_id;
235a491581fSWei Hu };
236a491581fSWei Hu 
237a491581fSWei Hu #define NDIS_PKTINFOID_SZ		sizeof(struct packet_info_id)
238a491581fSWei Hu 
239a491581fSWei Hu 
24015516c77SSepherosa Ziehau struct hn_rxinfo {
241a491581fSWei Hu 	const uint32_t			*vlan_info;
242a491581fSWei Hu 	const uint32_t			*csum_info;
243a491581fSWei Hu 	const uint32_t			*hash_info;
244a491581fSWei Hu 	const uint32_t			*hash_value;
245a491581fSWei Hu 	const struct packet_info_id	*pktinfo_id;
24615516c77SSepherosa Ziehau };
24715516c77SSepherosa Ziehau 
248962f0357SSepherosa Ziehau struct hn_rxvf_setarg {
2495bdfd3fdSDexuan Cui 	struct hn_rx_ring	*rxr;
250*4db5958aSJustin Hibbits 	if_t			vf_ifp;
2515bdfd3fdSDexuan Cui };
2525bdfd3fdSDexuan Cui 
25315516c77SSepherosa Ziehau #define HN_RXINFO_VLAN			0x0001
25415516c77SSepherosa Ziehau #define HN_RXINFO_CSUM			0x0002
25515516c77SSepherosa Ziehau #define HN_RXINFO_HASHINF		0x0004
25615516c77SSepherosa Ziehau #define HN_RXINFO_HASHVAL		0x0008
257a491581fSWei Hu #define HN_RXINFO_PKTINFO_ID		0x0010
25815516c77SSepherosa Ziehau #define HN_RXINFO_ALL			\
25915516c77SSepherosa Ziehau 	(HN_RXINFO_VLAN |		\
26015516c77SSepherosa Ziehau 	 HN_RXINFO_CSUM |		\
26115516c77SSepherosa Ziehau 	 HN_RXINFO_HASHINF |		\
262a491581fSWei Hu 	 HN_RXINFO_HASHVAL |		\
263a491581fSWei Hu 	 HN_RXINFO_PKTINFO_ID)
26415516c77SSepherosa Ziehau 
26515516c77SSepherosa Ziehau static int			hn_probe(device_t);
26615516c77SSepherosa Ziehau static int			hn_attach(device_t);
26715516c77SSepherosa Ziehau static int			hn_detach(device_t);
26815516c77SSepherosa Ziehau static int			hn_shutdown(device_t);
26915516c77SSepherosa Ziehau static void			hn_chan_callback(struct vmbus_channel *,
27015516c77SSepherosa Ziehau 				    void *);
27115516c77SSepherosa Ziehau 
27215516c77SSepherosa Ziehau static void			hn_init(void *);
273*4db5958aSJustin Hibbits static int			hn_ioctl(if_t, u_long, caddr_t);
27423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
275*4db5958aSJustin Hibbits static void			hn_start(if_t);
27623bf9e15SSepherosa Ziehau #endif
277*4db5958aSJustin Hibbits static int			hn_transmit(if_t, struct mbuf *);
278*4db5958aSJustin Hibbits static void			hn_xmit_qflush(if_t);
279*4db5958aSJustin Hibbits static int			hn_ifmedia_upd(if_t);
280*4db5958aSJustin Hibbits static void			hn_ifmedia_sts(if_t,
28115516c77SSepherosa Ziehau 				    struct ifmediareq *);
28215516c77SSepherosa Ziehau 
283*4db5958aSJustin Hibbits static void			hn_ifnet_event(void *, if_t, int);
284*4db5958aSJustin Hibbits static void			hn_ifaddr_event(void *, if_t);
285*4db5958aSJustin Hibbits static void			hn_ifnet_attevent(void *, if_t);
286*4db5958aSJustin Hibbits static void			hn_ifnet_detevent(void *, if_t);
287*4db5958aSJustin Hibbits static void			hn_ifnet_lnkevent(void *, if_t, int);
288499c3e17SSepherosa Ziehau 
289962f0357SSepherosa Ziehau static bool			hn_ismyvf(const struct hn_softc *,
290*4db5958aSJustin Hibbits 				    const if_t);
291962f0357SSepherosa Ziehau static void			hn_rxvf_change(struct hn_softc *,
292*4db5958aSJustin Hibbits 				    if_t, bool);
293*4db5958aSJustin Hibbits static void			hn_rxvf_set(struct hn_softc *, if_t);
294962f0357SSepherosa Ziehau static void			hn_rxvf_set_task(void *, int);
295*4db5958aSJustin Hibbits static void			hn_xpnt_vf_input(if_t, struct mbuf *);
2969c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_iocsetflags(struct hn_softc *);
2979c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_iocsetcaps(struct hn_softc *,
2989c6cae24SSepherosa Ziehau 				    struct ifreq *);
2999c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_saveifflags(struct hn_softc *);
3009c6cae24SSepherosa Ziehau static bool			hn_xpnt_vf_isready(struct hn_softc *);
3019c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_setready(struct hn_softc *);
3029c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_init_taskfunc(void *, int);
3039c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_init(struct hn_softc *);
304a97fff19SSepherosa Ziehau static void			hn_xpnt_vf_setenable(struct hn_softc *);
305a97fff19SSepherosa Ziehau static void			hn_xpnt_vf_setdisable(struct hn_softc *, bool);
306642ec226SSepherosa Ziehau static void			hn_vf_rss_fixup(struct hn_softc *, bool);
307642ec226SSepherosa Ziehau static void			hn_vf_rss_restore(struct hn_softc *);
308962f0357SSepherosa Ziehau 
30915516c77SSepherosa Ziehau static int			hn_rndis_rxinfo(const void *, int,
31015516c77SSepherosa Ziehau 				    struct hn_rxinfo *);
31115516c77SSepherosa Ziehau static void			hn_rndis_rx_data(struct hn_rx_ring *,
31215516c77SSepherosa Ziehau 				    const void *, int);
31315516c77SSepherosa Ziehau static void			hn_rndis_rx_status(struct hn_softc *,
31415516c77SSepherosa Ziehau 				    const void *, int);
315b3b75d9cSSepherosa Ziehau static void			hn_rndis_init_fixat(struct hn_softc *, int);
31615516c77SSepherosa Ziehau 
31715516c77SSepherosa Ziehau static void			hn_nvs_handle_notify(struct hn_softc *,
31815516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
31915516c77SSepherosa Ziehau static void			hn_nvs_handle_comp(struct hn_softc *,
32015516c77SSepherosa Ziehau 				    struct vmbus_channel *,
32115516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
32215516c77SSepherosa Ziehau static void			hn_nvs_handle_rxbuf(struct hn_rx_ring *,
32315516c77SSepherosa Ziehau 				    struct vmbus_channel *,
32415516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
32515516c77SSepherosa Ziehau static void			hn_nvs_ack_rxbuf(struct hn_rx_ring *,
32615516c77SSepherosa Ziehau 				    struct vmbus_channel *, uint64_t);
32715516c77SSepherosa Ziehau 
32815516c77SSepherosa Ziehau static int			hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS);
32915516c77SSepherosa Ziehau static int			hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS);
33015516c77SSepherosa Ziehau static int			hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS);
33115516c77SSepherosa Ziehau static int			hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS);
33215516c77SSepherosa Ziehau static int			hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS);
33315516c77SSepherosa Ziehau static int			hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
33415516c77SSepherosa Ziehau static int			hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
33515516c77SSepherosa Ziehau static int			hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS);
33615516c77SSepherosa Ziehau static int			hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS);
33715516c77SSepherosa Ziehau static int			hn_caps_sysctl(SYSCTL_HANDLER_ARGS);
33815516c77SSepherosa Ziehau static int			hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS);
33915516c77SSepherosa Ziehau static int			hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS);
34034d68912SSepherosa Ziehau #ifndef RSS
34115516c77SSepherosa Ziehau static int			hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS);
34215516c77SSepherosa Ziehau static int			hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS);
34334d68912SSepherosa Ziehau #endif
34415516c77SSepherosa Ziehau static int			hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS);
345642ec226SSepherosa Ziehau static int			hn_rss_hcap_sysctl(SYSCTL_HANDLER_ARGS);
346642ec226SSepherosa Ziehau static int			hn_rss_mbuf_sysctl(SYSCTL_HANDLER_ARGS);
347dc13fee6SSepherosa Ziehau static int			hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS);
348dc13fee6SSepherosa Ziehau static int			hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS);
349dc13fee6SSepherosa Ziehau static int			hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS);
350dc13fee6SSepherosa Ziehau static int			hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
3516c1204dfSSepherosa Ziehau static int			hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
35240d60d6eSDexuan Cui static int			hn_vf_sysctl(SYSCTL_HANDLER_ARGS);
353499c3e17SSepherosa Ziehau static int			hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS);
354499c3e17SSepherosa Ziehau static int			hn_vflist_sysctl(SYSCTL_HANDLER_ARGS);
355499c3e17SSepherosa Ziehau static int			hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS);
3569c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS);
3579c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS);
35815516c77SSepherosa Ziehau 
3595bdfd3fdSDexuan Cui static void			hn_stop(struct hn_softc *, bool);
36015516c77SSepherosa Ziehau static void			hn_init_locked(struct hn_softc *);
36115516c77SSepherosa Ziehau static int			hn_chan_attach(struct hn_softc *,
36215516c77SSepherosa Ziehau 				    struct vmbus_channel *);
36315516c77SSepherosa Ziehau static void			hn_chan_detach(struct hn_softc *,
36415516c77SSepherosa Ziehau 				    struct vmbus_channel *);
36515516c77SSepherosa Ziehau static int			hn_attach_subchans(struct hn_softc *);
36615516c77SSepherosa Ziehau static void			hn_detach_allchans(struct hn_softc *);
36715516c77SSepherosa Ziehau static void			hn_chan_rollup(struct hn_rx_ring *,
36815516c77SSepherosa Ziehau 				    struct hn_tx_ring *);
36915516c77SSepherosa Ziehau static void			hn_set_ring_inuse(struct hn_softc *, int);
37015516c77SSepherosa Ziehau static int			hn_synth_attach(struct hn_softc *, int);
37115516c77SSepherosa Ziehau static void			hn_synth_detach(struct hn_softc *);
37215516c77SSepherosa Ziehau static int			hn_synth_alloc_subchans(struct hn_softc *,
37315516c77SSepherosa Ziehau 				    int *);
3742494d735SSepherosa Ziehau static bool			hn_synth_attachable(const struct hn_softc *);
37515516c77SSepherosa Ziehau static void			hn_suspend(struct hn_softc *);
37615516c77SSepherosa Ziehau static void			hn_suspend_data(struct hn_softc *);
37715516c77SSepherosa Ziehau static void			hn_suspend_mgmt(struct hn_softc *);
37815516c77SSepherosa Ziehau static void			hn_resume(struct hn_softc *);
37915516c77SSepherosa Ziehau static void			hn_resume_data(struct hn_softc *);
38015516c77SSepherosa Ziehau static void			hn_resume_mgmt(struct hn_softc *);
38115516c77SSepherosa Ziehau static void			hn_suspend_mgmt_taskfunc(void *, int);
38225641fc7SSepherosa Ziehau static void			hn_chan_drain(struct hn_softc *,
38325641fc7SSepherosa Ziehau 				    struct vmbus_channel *);
384b3b75d9cSSepherosa Ziehau static void			hn_disable_rx(struct hn_softc *);
385b3b75d9cSSepherosa Ziehau static void			hn_drain_rxtx(struct hn_softc *, int);
3866c1204dfSSepherosa Ziehau static void			hn_polling(struct hn_softc *, u_int);
3876c1204dfSSepherosa Ziehau static void			hn_chan_polling(struct vmbus_channel *, u_int);
3889c6cae24SSepherosa Ziehau static void			hn_mtu_change_fixup(struct hn_softc *);
38915516c77SSepherosa Ziehau 
39015516c77SSepherosa Ziehau static void			hn_update_link_status(struct hn_softc *);
39115516c77SSepherosa Ziehau static void			hn_change_network(struct hn_softc *);
39215516c77SSepherosa Ziehau static void			hn_link_taskfunc(void *, int);
39315516c77SSepherosa Ziehau static void			hn_netchg_init_taskfunc(void *, int);
39415516c77SSepherosa Ziehau static void			hn_netchg_status_taskfunc(void *, int);
39515516c77SSepherosa Ziehau static void			hn_link_status(struct hn_softc *);
39615516c77SSepherosa Ziehau 
39715516c77SSepherosa Ziehau static int			hn_create_rx_data(struct hn_softc *, int);
39815516c77SSepherosa Ziehau static void			hn_destroy_rx_data(struct hn_softc *);
39915516c77SSepherosa Ziehau static int			hn_check_iplen(const struct mbuf *, int);
400db76829bSSepherosa Ziehau static void			hn_rxpkt_proto(const struct mbuf *, int *, int *);
401f1b0a43fSSepherosa Ziehau static int			hn_set_rxfilter(struct hn_softc *, uint32_t);
402c08f7b2cSSepherosa Ziehau static int			hn_rxfilter_config(struct hn_softc *);
40315516c77SSepherosa Ziehau static int			hn_rss_reconfig(struct hn_softc *);
404afd4971bSSepherosa Ziehau static void			hn_rss_ind_fixup(struct hn_softc *);
405642ec226SSepherosa Ziehau static void			hn_rss_mbuf_hash(struct hn_softc *, uint32_t);
406a491581fSWei Hu static int			hn_rxpkt(struct hn_rx_ring *);
407642ec226SSepherosa Ziehau static uint32_t			hn_rss_type_fromndis(uint32_t);
408642ec226SSepherosa Ziehau static uint32_t			hn_rss_type_tondis(uint32_t);
40915516c77SSepherosa Ziehau 
41015516c77SSepherosa Ziehau static int			hn_tx_ring_create(struct hn_softc *, int);
41115516c77SSepherosa Ziehau static void			hn_tx_ring_destroy(struct hn_tx_ring *);
41215516c77SSepherosa Ziehau static int			hn_create_tx_data(struct hn_softc *, int);
41315516c77SSepherosa Ziehau static void			hn_fixup_tx_data(struct hn_softc *);
414db76829bSSepherosa Ziehau static void			hn_fixup_rx_data(struct hn_softc *);
41515516c77SSepherosa Ziehau static void			hn_destroy_tx_data(struct hn_softc *);
41615516c77SSepherosa Ziehau static void			hn_txdesc_dmamap_destroy(struct hn_txdesc *);
41725641fc7SSepherosa Ziehau static void			hn_txdesc_gc(struct hn_tx_ring *,
41825641fc7SSepherosa Ziehau 				    struct hn_txdesc *);
419*4db5958aSJustin Hibbits static int			hn_encap(if_t, struct hn_tx_ring *,
42015516c77SSepherosa Ziehau 				    struct hn_txdesc *, struct mbuf **);
421*4db5958aSJustin Hibbits static int			hn_txpkt(if_t, struct hn_tx_ring *,
42215516c77SSepherosa Ziehau 				    struct hn_txdesc *);
42315516c77SSepherosa Ziehau static void			hn_set_chim_size(struct hn_softc *, int);
42415516c77SSepherosa Ziehau static void			hn_set_tso_maxsize(struct hn_softc *, int, int);
42515516c77SSepherosa Ziehau static bool			hn_tx_ring_pending(struct hn_tx_ring *);
42615516c77SSepherosa Ziehau static void			hn_tx_ring_qflush(struct hn_tx_ring *);
42715516c77SSepherosa Ziehau static void			hn_resume_tx(struct hn_softc *, int);
428dc13fee6SSepherosa Ziehau static void			hn_set_txagg(struct hn_softc *);
429*4db5958aSJustin Hibbits static void			*hn_try_txagg(if_t,
430dc13fee6SSepherosa Ziehau 				    struct hn_tx_ring *, struct hn_txdesc *,
431dc13fee6SSepherosa Ziehau 				    int);
43215516c77SSepherosa Ziehau static int			hn_get_txswq_depth(const struct hn_tx_ring *);
43315516c77SSepherosa Ziehau static void			hn_txpkt_done(struct hn_nvs_sendctx *,
43415516c77SSepherosa Ziehau 				    struct hn_softc *, struct vmbus_channel *,
43515516c77SSepherosa Ziehau 				    const void *, int);
43615516c77SSepherosa Ziehau static int			hn_txpkt_sglist(struct hn_tx_ring *,
43715516c77SSepherosa Ziehau 				    struct hn_txdesc *);
43815516c77SSepherosa Ziehau static int			hn_txpkt_chim(struct hn_tx_ring *,
43915516c77SSepherosa Ziehau 				    struct hn_txdesc *);
44015516c77SSepherosa Ziehau static int			hn_xmit(struct hn_tx_ring *, int);
44115516c77SSepherosa Ziehau static void			hn_xmit_taskfunc(void *, int);
44215516c77SSepherosa Ziehau static void			hn_xmit_txeof(struct hn_tx_ring *);
44315516c77SSepherosa Ziehau static void			hn_xmit_txeof_taskfunc(void *, int);
44423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
44515516c77SSepherosa Ziehau static int			hn_start_locked(struct hn_tx_ring *, int);
44615516c77SSepherosa Ziehau static void			hn_start_taskfunc(void *, int);
44715516c77SSepherosa Ziehau static void			hn_start_txeof(struct hn_tx_ring *);
44815516c77SSepherosa Ziehau static void			hn_start_txeof_taskfunc(void *, int);
44923bf9e15SSepherosa Ziehau #endif
45015516c77SSepherosa Ziehau 
45180c3eb7bSWei Hu static int			hn_rsc_sysctl(SYSCTL_HANDLER_ARGS);
45280c3eb7bSWei Hu 
45315516c77SSepherosa Ziehau SYSCTL_NODE(_hw, OID_AUTO, hn, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
45415516c77SSepherosa Ziehau     "Hyper-V network interface");
45515516c77SSepherosa Ziehau 
456b15a632cSGordon Bergling /* Trust tcp segment verification on host side. */
45715516c77SSepherosa Ziehau static int			hn_trust_hosttcp = 1;
45815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hosttcp, CTLFLAG_RDTUN,
45915516c77SSepherosa Ziehau     &hn_trust_hosttcp, 0,
460b15a632cSGordon Bergling     "Trust tcp segment verification on host side, "
46115516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
46215516c77SSepherosa Ziehau 
46315516c77SSepherosa Ziehau /* Trust udp datagrams verification on host side. */
46415516c77SSepherosa Ziehau static int			hn_trust_hostudp = 1;
46515516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostudp, CTLFLAG_RDTUN,
46615516c77SSepherosa Ziehau     &hn_trust_hostudp, 0,
46715516c77SSepherosa Ziehau     "Trust udp datagram verification on host side, "
46815516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
46915516c77SSepherosa Ziehau 
47015516c77SSepherosa Ziehau /* Trust ip packets verification on host side. */
47115516c77SSepherosa Ziehau static int			hn_trust_hostip = 1;
47215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostip, CTLFLAG_RDTUN,
47315516c77SSepherosa Ziehau     &hn_trust_hostip, 0,
47415516c77SSepherosa Ziehau     "Trust ip packet verification on host side, "
47515516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
47615516c77SSepherosa Ziehau 
4772be266caSSepherosa Ziehau /*
4782be266caSSepherosa Ziehau  * Offload UDP/IPv4 checksum.
4792be266caSSepherosa Ziehau  */
4802be266caSSepherosa Ziehau static int			hn_enable_udp4cs = 1;
4812be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp4cs, CTLFLAG_RDTUN,
4822be266caSSepherosa Ziehau     &hn_enable_udp4cs, 0, "Offload UDP/IPv4 checksum");
4832be266caSSepherosa Ziehau 
4842be266caSSepherosa Ziehau /*
4852be266caSSepherosa Ziehau  * Offload UDP/IPv6 checksum.
4862be266caSSepherosa Ziehau  */
4872be266caSSepherosa Ziehau static int			hn_enable_udp6cs = 1;
4882be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp6cs, CTLFLAG_RDTUN,
4892be266caSSepherosa Ziehau     &hn_enable_udp6cs, 0, "Offload UDP/IPv6 checksum");
4902be266caSSepherosa Ziehau 
4912be266caSSepherosa Ziehau /* Stats. */
4922be266caSSepherosa Ziehau static counter_u64_t		hn_udpcs_fixup;
4932be266caSSepherosa Ziehau SYSCTL_COUNTER_U64(_hw_hn, OID_AUTO, udpcs_fixup, CTLFLAG_RW,
4942be266caSSepherosa Ziehau     &hn_udpcs_fixup, "# of UDP checksum fixup");
4952be266caSSepherosa Ziehau 
4962be266caSSepherosa Ziehau /*
4972be266caSSepherosa Ziehau  * See hn_set_hlen().
4982be266caSSepherosa Ziehau  *
4992be266caSSepherosa Ziehau  * This value is for Azure.  For Hyper-V, set this above
5002be266caSSepherosa Ziehau  * 65536 to disable UDP datagram checksum fixup.
5012be266caSSepherosa Ziehau  */
5022be266caSSepherosa Ziehau static int			hn_udpcs_fixup_mtu = 1420;
5032be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, udpcs_fixup_mtu, CTLFLAG_RWTUN,
5042be266caSSepherosa Ziehau     &hn_udpcs_fixup_mtu, 0, "UDP checksum fixup MTU threshold");
5052be266caSSepherosa Ziehau 
50615516c77SSepherosa Ziehau /* Limit TSO burst size */
50715516c77SSepherosa Ziehau static int			hn_tso_maxlen = IP_MAXPACKET;
50815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tso_maxlen, CTLFLAG_RDTUN,
50915516c77SSepherosa Ziehau     &hn_tso_maxlen, 0, "TSO burst limit");
51015516c77SSepherosa Ziehau 
51115516c77SSepherosa Ziehau /* Limit chimney send size */
51215516c77SSepherosa Ziehau static int			hn_tx_chimney_size = 0;
51315516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_chimney_size, CTLFLAG_RDTUN,
51415516c77SSepherosa Ziehau     &hn_tx_chimney_size, 0, "Chimney send packet size limit");
51515516c77SSepherosa Ziehau 
51615516c77SSepherosa Ziehau /* Limit the size of packet for direct transmission */
51715516c77SSepherosa Ziehau static int			hn_direct_tx_size = HN_DIRECT_TX_SIZE_DEF;
51815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, direct_tx_size, CTLFLAG_RDTUN,
51915516c77SSepherosa Ziehau     &hn_direct_tx_size, 0, "Size of the packet for direct transmission");
52015516c77SSepherosa Ziehau 
52115516c77SSepherosa Ziehau /* # of LRO entries per RX ring */
52215516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
52315516c77SSepherosa Ziehau static int			hn_lro_entry_count = HN_LROENT_CNT_DEF;
52415516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, lro_entry_count, CTLFLAG_RDTUN,
52515516c77SSepherosa Ziehau     &hn_lro_entry_count, 0, "LRO entry count");
52615516c77SSepherosa Ziehau #endif
52715516c77SSepherosa Ziehau 
528fdd0222aSSepherosa Ziehau static int			hn_tx_taskq_cnt = 1;
529fdd0222aSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_cnt, CTLFLAG_RDTUN,
530fdd0222aSSepherosa Ziehau     &hn_tx_taskq_cnt, 0, "# of TX taskqueues");
531fdd0222aSSepherosa Ziehau 
5320e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_INDEP	0
5330e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_GLOBAL	1
5340e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_EVTTQ	2
5350e11868dSSepherosa Ziehau 
5360e11868dSSepherosa Ziehau static int			hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
5370e11868dSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_mode, CTLFLAG_RDTUN,
5380e11868dSSepherosa Ziehau     &hn_tx_taskq_mode, 0, "TX taskqueue modes: "
5390e11868dSSepherosa Ziehau     "0 - independent, 1 - share global tx taskqs, 2 - share event taskqs");
5400e11868dSSepherosa Ziehau 
54115516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
54215516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 0;
54315516c77SSepherosa Ziehau #else
54415516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 1;
54515516c77SSepherosa Ziehau #endif
54615516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_txdesc_bufring, CTLFLAG_RD,
54715516c77SSepherosa Ziehau     &hn_use_txdesc_bufring, 0, "Use buf_ring for TX descriptors");
54815516c77SSepherosa Ziehau 
54923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
55015516c77SSepherosa Ziehau /* Use ifnet.if_start instead of ifnet.if_transmit */
55115516c77SSepherosa Ziehau static int			hn_use_if_start = 0;
55215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_if_start, CTLFLAG_RDTUN,
55315516c77SSepherosa Ziehau     &hn_use_if_start, 0, "Use if_start TX method");
55423bf9e15SSepherosa Ziehau #endif
55515516c77SSepherosa Ziehau 
55615516c77SSepherosa Ziehau /* # of channels to use */
55715516c77SSepherosa Ziehau static int			hn_chan_cnt = 0;
55815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, chan_cnt, CTLFLAG_RDTUN,
55915516c77SSepherosa Ziehau     &hn_chan_cnt, 0,
56015516c77SSepherosa Ziehau     "# of channels to use; each channel has one RX ring and one TX ring");
56115516c77SSepherosa Ziehau 
56215516c77SSepherosa Ziehau /* # of transmit rings to use */
56315516c77SSepherosa Ziehau static int			hn_tx_ring_cnt = 0;
56415516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_ring_cnt, CTLFLAG_RDTUN,
56515516c77SSepherosa Ziehau     &hn_tx_ring_cnt, 0, "# of TX rings to use");
56615516c77SSepherosa Ziehau 
56715516c77SSepherosa Ziehau /* Software TX ring deptch */
56815516c77SSepherosa Ziehau static int			hn_tx_swq_depth = 0;
56915516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_swq_depth, CTLFLAG_RDTUN,
57015516c77SSepherosa Ziehau     &hn_tx_swq_depth, 0, "Depth of IFQ or BUFRING");
57115516c77SSepherosa Ziehau 
57215516c77SSepherosa Ziehau /* Enable sorted LRO, and the depth of the per-channel mbuf queue */
57315516c77SSepherosa Ziehau static u_int			hn_lro_mbufq_depth = 0;
57415516c77SSepherosa Ziehau SYSCTL_UINT(_hw_hn, OID_AUTO, lro_mbufq_depth, CTLFLAG_RDTUN,
57515516c77SSepherosa Ziehau     &hn_lro_mbufq_depth, 0, "Depth of LRO mbuf queue");
57615516c77SSepherosa Ziehau 
577dc13fee6SSepherosa Ziehau /* Packet transmission aggregation size limit */
578dc13fee6SSepherosa Ziehau static int			hn_tx_agg_size = -1;
579dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_size, CTLFLAG_RDTUN,
580dc13fee6SSepherosa Ziehau     &hn_tx_agg_size, 0, "Packet transmission aggregation size limit");
581dc13fee6SSepherosa Ziehau 
582dc13fee6SSepherosa Ziehau /* Packet transmission aggregation count limit */
583fa915c4dSSepherosa Ziehau static int			hn_tx_agg_pkts = -1;
584dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_pkts, CTLFLAG_RDTUN,
585dc13fee6SSepherosa Ziehau     &hn_tx_agg_pkts, 0, "Packet transmission aggregation packet limit");
586dc13fee6SSepherosa Ziehau 
587499c3e17SSepherosa Ziehau /* VF list */
5887029da5cSPawel Biernacki SYSCTL_PROC(_hw_hn, OID_AUTO, vflist,
5897029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 0, 0,
5907029da5cSPawel Biernacki     hn_vflist_sysctl, "A",
5917029da5cSPawel Biernacki     "VF list");
592499c3e17SSepherosa Ziehau 
593499c3e17SSepherosa Ziehau /* VF mapping */
5947029da5cSPawel Biernacki SYSCTL_PROC(_hw_hn, OID_AUTO, vfmap,
5957029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 0, 0,
5967029da5cSPawel Biernacki     hn_vfmap_sysctl, "A",
5977029da5cSPawel Biernacki     "VF mapping");
598499c3e17SSepherosa Ziehau 
5999c6cae24SSepherosa Ziehau /* Transparent VF */
60078e46963SSepherosa Ziehau static int			hn_xpnt_vf = 1;
6019c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_transparent, CTLFLAG_RDTUN,
6029c6cae24SSepherosa Ziehau     &hn_xpnt_vf, 0, "Transparent VF mod");
6039c6cae24SSepherosa Ziehau 
6049c6cae24SSepherosa Ziehau /* Accurate BPF support for Transparent VF */
6059c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf = 0;
6069c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_accbpf, CTLFLAG_RDTUN,
6079c6cae24SSepherosa Ziehau     &hn_xpnt_vf_accbpf, 0, "Accurate BPF for transparent VF");
6089c6cae24SSepherosa Ziehau 
6099c6cae24SSepherosa Ziehau /* Extra wait for transparent VF attach routing; unit seconds. */
6109c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
6119c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_attwait, CTLFLAG_RWTUN,
6129c6cae24SSepherosa Ziehau     &hn_xpnt_vf_attwait, 0,
6139c6cae24SSepherosa Ziehau     "Extra wait for transparent VF attach routing; unit: seconds");
6149c6cae24SSepherosa Ziehau 
61515516c77SSepherosa Ziehau static u_int			hn_cpu_index;	/* next CPU for channel */
616fdd0222aSSepherosa Ziehau static struct taskqueue		**hn_tx_taskque;/* shared TX taskqueues */
61715516c77SSepherosa Ziehau 
618499c3e17SSepherosa Ziehau static struct rmlock		hn_vfmap_lock;
619499c3e17SSepherosa Ziehau static int			hn_vfmap_size;
620*4db5958aSJustin Hibbits static if_t			*hn_vfmap;
621499c3e17SSepherosa Ziehau 
62234d68912SSepherosa Ziehau #ifndef RSS
62315516c77SSepherosa Ziehau static const uint8_t
62415516c77SSepherosa Ziehau hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
62515516c77SSepherosa Ziehau 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
62615516c77SSepherosa Ziehau 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
62715516c77SSepherosa Ziehau 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
62815516c77SSepherosa Ziehau 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
62915516c77SSepherosa Ziehau 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
63015516c77SSepherosa Ziehau };
63134d68912SSepherosa Ziehau #endif	/* !RSS */
63215516c77SSepherosa Ziehau 
633c2d50b26SSepherosa Ziehau static const struct hyperv_guid	hn_guid = {
634c2d50b26SSepherosa Ziehau 	.hv_guid = {
635c2d50b26SSepherosa Ziehau 	    0x63, 0x51, 0x61, 0xf8, 0x3e, 0xdf, 0xc5, 0x46,
636c2d50b26SSepherosa Ziehau 	    0x91, 0x3f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e }
637c2d50b26SSepherosa Ziehau };
638c2d50b26SSepherosa Ziehau 
63915516c77SSepherosa Ziehau static device_method_t hn_methods[] = {
64015516c77SSepherosa Ziehau 	/* Device interface */
64115516c77SSepherosa Ziehau 	DEVMETHOD(device_probe,		hn_probe),
64215516c77SSepherosa Ziehau 	DEVMETHOD(device_attach,	hn_attach),
64315516c77SSepherosa Ziehau 	DEVMETHOD(device_detach,	hn_detach),
64415516c77SSepherosa Ziehau 	DEVMETHOD(device_shutdown,	hn_shutdown),
64515516c77SSepherosa Ziehau 	DEVMETHOD_END
64615516c77SSepherosa Ziehau };
64715516c77SSepherosa Ziehau 
64815516c77SSepherosa Ziehau static driver_t hn_driver = {
64915516c77SSepherosa Ziehau 	"hn",
65015516c77SSepherosa Ziehau 	hn_methods,
65115516c77SSepherosa Ziehau 	sizeof(struct hn_softc)
65215516c77SSepherosa Ziehau };
65315516c77SSepherosa Ziehau 
654c1cef544SJohn Baldwin DRIVER_MODULE(hn, vmbus, hn_driver, 0, 0);
65515516c77SSepherosa Ziehau MODULE_VERSION(hn, 1);
65615516c77SSepherosa Ziehau MODULE_DEPEND(hn, vmbus, 1, 1, 1);
65715516c77SSepherosa Ziehau 
65815516c77SSepherosa Ziehau static void
65915516c77SSepherosa Ziehau hn_set_lro_lenlim(struct hn_softc *sc, int lenlim)
66015516c77SSepherosa Ziehau {
66115516c77SSepherosa Ziehau 	int i;
66215516c77SSepherosa Ziehau 
663a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
66415516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_length_lim = lenlim;
66515516c77SSepherosa Ziehau }
66615516c77SSepherosa Ziehau 
66715516c77SSepherosa Ziehau static int
66815516c77SSepherosa Ziehau hn_txpkt_sglist(struct hn_tx_ring *txr, struct hn_txdesc *txd)
66915516c77SSepherosa Ziehau {
67015516c77SSepherosa Ziehau 
67115516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
67215516c77SSepherosa Ziehau 	    txd->chim_size == 0, ("invalid rndis sglist txd"));
67315516c77SSepherosa Ziehau 	return (hn_nvs_send_rndis_sglist(txr->hn_chan, HN_NVS_RNDIS_MTYPE_DATA,
67415516c77SSepherosa Ziehau 	    &txd->send_ctx, txr->hn_gpa, txr->hn_gpa_cnt));
67515516c77SSepherosa Ziehau }
67615516c77SSepherosa Ziehau 
67715516c77SSepherosa Ziehau static int
67815516c77SSepherosa Ziehau hn_txpkt_chim(struct hn_tx_ring *txr, struct hn_txdesc *txd)
67915516c77SSepherosa Ziehau {
68015516c77SSepherosa Ziehau 	struct hn_nvs_rndis rndis;
68115516c77SSepherosa Ziehau 
68215516c77SSepherosa Ziehau 	KASSERT(txd->chim_index != HN_NVS_CHIM_IDX_INVALID &&
68315516c77SSepherosa Ziehau 	    txd->chim_size > 0, ("invalid rndis chim txd"));
68415516c77SSepherosa Ziehau 
68515516c77SSepherosa Ziehau 	rndis.nvs_type = HN_NVS_TYPE_RNDIS;
68615516c77SSepherosa Ziehau 	rndis.nvs_rndis_mtype = HN_NVS_RNDIS_MTYPE_DATA;
68715516c77SSepherosa Ziehau 	rndis.nvs_chim_idx = txd->chim_index;
68815516c77SSepherosa Ziehau 	rndis.nvs_chim_sz = txd->chim_size;
68915516c77SSepherosa Ziehau 
69015516c77SSepherosa Ziehau 	return (hn_nvs_send(txr->hn_chan, VMBUS_CHANPKT_FLAG_RC,
69115516c77SSepherosa Ziehau 	    &rndis, sizeof(rndis), &txd->send_ctx));
69215516c77SSepherosa Ziehau }
69315516c77SSepherosa Ziehau 
69415516c77SSepherosa Ziehau static __inline uint32_t
69515516c77SSepherosa Ziehau hn_chim_alloc(struct hn_softc *sc)
69615516c77SSepherosa Ziehau {
69715516c77SSepherosa Ziehau 	int i, bmap_cnt = sc->hn_chim_bmap_cnt;
69815516c77SSepherosa Ziehau 	u_long *bmap = sc->hn_chim_bmap;
69915516c77SSepherosa Ziehau 	uint32_t ret = HN_NVS_CHIM_IDX_INVALID;
70015516c77SSepherosa Ziehau 
70115516c77SSepherosa Ziehau 	for (i = 0; i < bmap_cnt; ++i) {
70215516c77SSepherosa Ziehau 		int idx;
70315516c77SSepherosa Ziehau 
70415516c77SSepherosa Ziehau 		idx = ffsl(~bmap[i]);
70515516c77SSepherosa Ziehau 		if (idx == 0)
70615516c77SSepherosa Ziehau 			continue;
70715516c77SSepherosa Ziehau 
70815516c77SSepherosa Ziehau 		--idx; /* ffsl is 1-based */
70915516c77SSepherosa Ziehau 		KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt,
71015516c77SSepherosa Ziehau 		    ("invalid i %d and idx %d", i, idx));
71115516c77SSepherosa Ziehau 
71215516c77SSepherosa Ziehau 		if (atomic_testandset_long(&bmap[i], idx))
71315516c77SSepherosa Ziehau 			continue;
71415516c77SSepherosa Ziehau 
71515516c77SSepherosa Ziehau 		ret = i * LONG_BIT + idx;
71615516c77SSepherosa Ziehau 		break;
71715516c77SSepherosa Ziehau 	}
71815516c77SSepherosa Ziehau 	return (ret);
71915516c77SSepherosa Ziehau }
72015516c77SSepherosa Ziehau 
72115516c77SSepherosa Ziehau static __inline void
72215516c77SSepherosa Ziehau hn_chim_free(struct hn_softc *sc, uint32_t chim_idx)
72315516c77SSepherosa Ziehau {
72415516c77SSepherosa Ziehau 	u_long mask;
72515516c77SSepherosa Ziehau 	uint32_t idx;
72615516c77SSepherosa Ziehau 
72715516c77SSepherosa Ziehau 	idx = chim_idx / LONG_BIT;
72815516c77SSepherosa Ziehau 	KASSERT(idx < sc->hn_chim_bmap_cnt,
72915516c77SSepherosa Ziehau 	    ("invalid chimney index 0x%x", chim_idx));
73015516c77SSepherosa Ziehau 
73115516c77SSepherosa Ziehau 	mask = 1UL << (chim_idx % LONG_BIT);
73215516c77SSepherosa Ziehau 	KASSERT(sc->hn_chim_bmap[idx] & mask,
73315516c77SSepherosa Ziehau 	    ("index bitmap 0x%lx, chimney index %u, "
73415516c77SSepherosa Ziehau 	     "bitmap idx %d, bitmask 0x%lx",
73515516c77SSepherosa Ziehau 	     sc->hn_chim_bmap[idx], chim_idx, idx, mask));
73615516c77SSepherosa Ziehau 
73715516c77SSepherosa Ziehau 	atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
73815516c77SSepherosa Ziehau }
73915516c77SSepherosa Ziehau 
740edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
741cc0c6ebcSSepherosa Ziehau 
742cc0c6ebcSSepherosa Ziehau #define PULLUP_HDR(m, len)				\
743cc0c6ebcSSepherosa Ziehau do {							\
744cc0c6ebcSSepherosa Ziehau 	if (__predict_false((m)->m_len < (len))) {	\
745cc0c6ebcSSepherosa Ziehau 		(m) = m_pullup((m), (len));		\
746cc0c6ebcSSepherosa Ziehau 		if ((m) == NULL)			\
747cc0c6ebcSSepherosa Ziehau 			return (NULL);			\
748cc0c6ebcSSepherosa Ziehau 	}						\
749cc0c6ebcSSepherosa Ziehau } while (0)
750cc0c6ebcSSepherosa Ziehau 
751edd3f315SSepherosa Ziehau /*
752edd3f315SSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
753edd3f315SSepherosa Ziehau  */
754edd3f315SSepherosa Ziehau static __inline struct mbuf *
755edd3f315SSepherosa Ziehau hn_tso_fixup(struct mbuf *m_head)
756edd3f315SSepherosa Ziehau {
757edd3f315SSepherosa Ziehau 	struct ether_vlan_header *evl;
758edd3f315SSepherosa Ziehau 	struct tcphdr *th;
759edd3f315SSepherosa Ziehau 	int ehlen;
760edd3f315SSepherosa Ziehau 
761edd3f315SSepherosa Ziehau 	KASSERT(M_WRITABLE(m_head), ("TSO mbuf not writable"));
762edd3f315SSepherosa Ziehau 
763edd3f315SSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
764edd3f315SSepherosa Ziehau 	evl = mtod(m_head, struct ether_vlan_header *);
765edd3f315SSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
766edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
767edd3f315SSepherosa Ziehau 	else
768edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
769c49d47daSSepherosa Ziehau 	m_head->m_pkthdr.l2hlen = ehlen;
770edd3f315SSepherosa Ziehau 
771edd3f315SSepherosa Ziehau #ifdef INET
772edd3f315SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
773edd3f315SSepherosa Ziehau 		struct ip *ip;
774edd3f315SSepherosa Ziehau 		int iphlen;
775edd3f315SSepherosa Ziehau 
776edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
777edd3f315SSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
778edd3f315SSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
779c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = iphlen;
780edd3f315SSepherosa Ziehau 
781edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
782edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + iphlen);
783edd3f315SSepherosa Ziehau 
784edd3f315SSepherosa Ziehau 		ip->ip_len = 0;
785edd3f315SSepherosa Ziehau 		ip->ip_sum = 0;
786edd3f315SSepherosa Ziehau 		th->th_sum = in_pseudo(ip->ip_src.s_addr,
787edd3f315SSepherosa Ziehau 		    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
788edd3f315SSepherosa Ziehau 	}
789edd3f315SSepherosa Ziehau #endif
790edd3f315SSepherosa Ziehau #if defined(INET6) && defined(INET)
791edd3f315SSepherosa Ziehau 	else
792edd3f315SSepherosa Ziehau #endif
793edd3f315SSepherosa Ziehau #ifdef INET6
794edd3f315SSepherosa Ziehau 	{
795edd3f315SSepherosa Ziehau 		struct ip6_hdr *ip6;
796edd3f315SSepherosa Ziehau 
797edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
798edd3f315SSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
799edd3f315SSepherosa Ziehau 		if (ip6->ip6_nxt != IPPROTO_TCP) {
800edd3f315SSepherosa Ziehau 			m_freem(m_head);
801edd3f315SSepherosa Ziehau 			return (NULL);
802edd3f315SSepherosa Ziehau 		}
803c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = sizeof(*ip6);
804edd3f315SSepherosa Ziehau 
805edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
806edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + sizeof(*ip6));
807edd3f315SSepherosa Ziehau 
808edd3f315SSepherosa Ziehau 		ip6->ip6_plen = 0;
809edd3f315SSepherosa Ziehau 		th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
810edd3f315SSepherosa Ziehau 	}
811edd3f315SSepherosa Ziehau #endif
812edd3f315SSepherosa Ziehau 	return (m_head);
813edd3f315SSepherosa Ziehau }
814cc0c6ebcSSepherosa Ziehau 
815cc0c6ebcSSepherosa Ziehau /*
816cc0c6ebcSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
817cc0c6ebcSSepherosa Ziehau  */
818cc0c6ebcSSepherosa Ziehau static __inline struct mbuf *
819c49d47daSSepherosa Ziehau hn_set_hlen(struct mbuf *m_head)
820cc0c6ebcSSepherosa Ziehau {
821cc0c6ebcSSepherosa Ziehau 	const struct ether_vlan_header *evl;
822cc0c6ebcSSepherosa Ziehau 	int ehlen;
823cc0c6ebcSSepherosa Ziehau 
824cc0c6ebcSSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
825cc0c6ebcSSepherosa Ziehau 	evl = mtod(m_head, const struct ether_vlan_header *);
826cc0c6ebcSSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
827cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
828cc0c6ebcSSepherosa Ziehau 	else
829cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
830c49d47daSSepherosa Ziehau 	m_head->m_pkthdr.l2hlen = ehlen;
831cc0c6ebcSSepherosa Ziehau 
832cc0c6ebcSSepherosa Ziehau #ifdef INET
833c49d47daSSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP_UDP)) {
834cc0c6ebcSSepherosa Ziehau 		const struct ip *ip;
835cc0c6ebcSSepherosa Ziehau 		int iphlen;
836cc0c6ebcSSepherosa Ziehau 
837cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
838cc0c6ebcSSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
839cc0c6ebcSSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
840c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = iphlen;
8412be266caSSepherosa Ziehau 
8422be266caSSepherosa Ziehau 		/*
8432be266caSSepherosa Ziehau 		 * UDP checksum offload does not work in Azure, if the
8442be266caSSepherosa Ziehau 		 * following conditions meet:
8452be266caSSepherosa Ziehau 		 * - sizeof(IP hdr + UDP hdr + payload) > 1420.
8462be266caSSepherosa Ziehau 		 * - IP_DF is not set in the IP hdr.
8472be266caSSepherosa Ziehau 		 *
8482be266caSSepherosa Ziehau 		 * Fallback to software checksum for these UDP datagrams.
8492be266caSSepherosa Ziehau 		 */
8502be266caSSepherosa Ziehau 		if ((m_head->m_pkthdr.csum_flags & CSUM_IP_UDP) &&
8512be266caSSepherosa Ziehau 		    m_head->m_pkthdr.len > hn_udpcs_fixup_mtu + ehlen &&
8522be266caSSepherosa Ziehau 		    (ntohs(ip->ip_off) & IP_DF) == 0) {
8532be266caSSepherosa Ziehau 			uint16_t off = ehlen + iphlen;
8542be266caSSepherosa Ziehau 
8552be266caSSepherosa Ziehau 			counter_u64_add(hn_udpcs_fixup, 1);
8562be266caSSepherosa Ziehau 			PULLUP_HDR(m_head, off + sizeof(struct udphdr));
8572be266caSSepherosa Ziehau 			*(uint16_t *)(m_head->m_data + off +
8582be266caSSepherosa Ziehau                             m_head->m_pkthdr.csum_data) = in_cksum_skip(
8592be266caSSepherosa Ziehau 			    m_head, m_head->m_pkthdr.len, off);
8602be266caSSepherosa Ziehau 			m_head->m_pkthdr.csum_flags &= ~CSUM_IP_UDP;
8612be266caSSepherosa Ziehau 		}
862cc0c6ebcSSepherosa Ziehau 	}
863cc0c6ebcSSepherosa Ziehau #endif
864cc0c6ebcSSepherosa Ziehau #if defined(INET6) && defined(INET)
865cc0c6ebcSSepherosa Ziehau 	else
866cc0c6ebcSSepherosa Ziehau #endif
867cc0c6ebcSSepherosa Ziehau #ifdef INET6
868cc0c6ebcSSepherosa Ziehau 	{
869cc0c6ebcSSepherosa Ziehau 		const struct ip6_hdr *ip6;
870cc0c6ebcSSepherosa Ziehau 
871cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
872cc0c6ebcSSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
873f0319886SWei Hu 		if (ip6->ip6_nxt != IPPROTO_TCP &&
874f0319886SWei Hu 		    ip6->ip6_nxt != IPPROTO_UDP) {
875c49d47daSSepherosa Ziehau 			m_freem(m_head);
876c49d47daSSepherosa Ziehau 			return (NULL);
877c49d47daSSepherosa Ziehau 		}
878c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = sizeof(*ip6);
879cc0c6ebcSSepherosa Ziehau 	}
880cc0c6ebcSSepherosa Ziehau #endif
881cc0c6ebcSSepherosa Ziehau 	return (m_head);
882cc0c6ebcSSepherosa Ziehau }
883cc0c6ebcSSepherosa Ziehau 
884c49d47daSSepherosa Ziehau /*
885c49d47daSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
886c49d47daSSepherosa Ziehau  */
887c49d47daSSepherosa Ziehau static __inline struct mbuf *
888c49d47daSSepherosa Ziehau hn_check_tcpsyn(struct mbuf *m_head, int *tcpsyn)
889c49d47daSSepherosa Ziehau {
890c49d47daSSepherosa Ziehau 	const struct tcphdr *th;
891c49d47daSSepherosa Ziehau 	int ehlen, iphlen;
892c49d47daSSepherosa Ziehau 
893c49d47daSSepherosa Ziehau 	*tcpsyn = 0;
894c49d47daSSepherosa Ziehau 	ehlen = m_head->m_pkthdr.l2hlen;
895c49d47daSSepherosa Ziehau 	iphlen = m_head->m_pkthdr.l3hlen;
896c49d47daSSepherosa Ziehau 
897c49d47daSSepherosa Ziehau 	PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
898c49d47daSSepherosa Ziehau 	th = mtodo(m_head, ehlen + iphlen);
899c49d47daSSepherosa Ziehau 	if (th->th_flags & TH_SYN)
900c49d47daSSepherosa Ziehau 		*tcpsyn = 1;
901c49d47daSSepherosa Ziehau 	return (m_head);
902c49d47daSSepherosa Ziehau }
903c49d47daSSepherosa Ziehau 
904cc0c6ebcSSepherosa Ziehau #undef PULLUP_HDR
905cc0c6ebcSSepherosa Ziehau 
906edd3f315SSepherosa Ziehau #endif	/* INET6 || INET */
907edd3f315SSepherosa Ziehau 
90815516c77SSepherosa Ziehau static int
909f1b0a43fSSepherosa Ziehau hn_set_rxfilter(struct hn_softc *sc, uint32_t filter)
910f1b0a43fSSepherosa Ziehau {
911f1b0a43fSSepherosa Ziehau 	int error = 0;
912f1b0a43fSSepherosa Ziehau 
913f1b0a43fSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
914f1b0a43fSSepherosa Ziehau 
915f1b0a43fSSepherosa Ziehau 	if (sc->hn_rx_filter != filter) {
916f1b0a43fSSepherosa Ziehau 		error = hn_rndis_set_rxfilter(sc, filter);
917f1b0a43fSSepherosa Ziehau 		if (!error)
918f1b0a43fSSepherosa Ziehau 			sc->hn_rx_filter = filter;
919f1b0a43fSSepherosa Ziehau 	}
920f1b0a43fSSepherosa Ziehau 	return (error);
921f1b0a43fSSepherosa Ziehau }
922f1b0a43fSSepherosa Ziehau 
923f1b0a43fSSepherosa Ziehau static int
924c08f7b2cSSepherosa Ziehau hn_rxfilter_config(struct hn_softc *sc)
92515516c77SSepherosa Ziehau {
926*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
92715516c77SSepherosa Ziehau 	uint32_t filter;
92815516c77SSepherosa Ziehau 
92915516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
93015516c77SSepherosa Ziehau 
9319c6cae24SSepherosa Ziehau 	/*
9329c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, we don't know how
9339c6cae24SSepherosa Ziehau 	 * its RX filter is configured, so stick the synthetic device in
9349c6cae24SSepherosa Ziehau 	 * the promiscous mode.
9359c6cae24SSepherosa Ziehau 	 */
936*4db5958aSJustin Hibbits 	if ((if_getflags(ifp) & IFF_PROMISC) || (sc->hn_flags & HN_FLAG_RXVF)) {
93715516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
93815516c77SSepherosa Ziehau 	} else {
93915516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_DIRECTED;
940*4db5958aSJustin Hibbits 		if (if_getflags(ifp) & IFF_BROADCAST)
94115516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_BROADCAST;
94215516c77SSepherosa Ziehau 		/* TODO: support multicast list */
943*4db5958aSJustin Hibbits 		if ((if_getflags(ifp) & IFF_ALLMULTI) ||
944*4db5958aSJustin Hibbits 		    !if_maddr_empty(ifp))
94515516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
94615516c77SSepherosa Ziehau 	}
947f1b0a43fSSepherosa Ziehau 	return (hn_set_rxfilter(sc, filter));
94815516c77SSepherosa Ziehau }
94915516c77SSepherosa Ziehau 
950dc13fee6SSepherosa Ziehau static void
951dc13fee6SSepherosa Ziehau hn_set_txagg(struct hn_softc *sc)
952dc13fee6SSepherosa Ziehau {
953dc13fee6SSepherosa Ziehau 	uint32_t size, pkts;
954dc13fee6SSepherosa Ziehau 	int i;
955dc13fee6SSepherosa Ziehau 
956dc13fee6SSepherosa Ziehau 	/*
957dc13fee6SSepherosa Ziehau 	 * Setup aggregation size.
958dc13fee6SSepherosa Ziehau 	 */
959dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_size < 0)
960dc13fee6SSepherosa Ziehau 		size = UINT32_MAX;
961dc13fee6SSepherosa Ziehau 	else
962dc13fee6SSepherosa Ziehau 		size = sc->hn_agg_size;
963dc13fee6SSepherosa Ziehau 
964dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_size < size)
965dc13fee6SSepherosa Ziehau 		size = sc->hn_rndis_agg_size;
966dc13fee6SSepherosa Ziehau 
967a4364cfeSSepherosa Ziehau 	/* NOTE: We only aggregate packets using chimney sending buffers. */
968a4364cfeSSepherosa Ziehau 	if (size > (uint32_t)sc->hn_chim_szmax)
969a4364cfeSSepherosa Ziehau 		size = sc->hn_chim_szmax;
970a4364cfeSSepherosa Ziehau 
971dc13fee6SSepherosa Ziehau 	if (size <= 2 * HN_PKTSIZE_MIN(sc->hn_rndis_agg_align)) {
972dc13fee6SSepherosa Ziehau 		/* Disable */
973dc13fee6SSepherosa Ziehau 		size = 0;
974dc13fee6SSepherosa Ziehau 		pkts = 0;
975dc13fee6SSepherosa Ziehau 		goto done;
976dc13fee6SSepherosa Ziehau 	}
977dc13fee6SSepherosa Ziehau 
978dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'int'. */
979dc13fee6SSepherosa Ziehau 	if (size > INT_MAX)
980dc13fee6SSepherosa Ziehau 		size = INT_MAX;
981dc13fee6SSepherosa Ziehau 
982dc13fee6SSepherosa Ziehau 	/*
983dc13fee6SSepherosa Ziehau 	 * Setup aggregation packet count.
984dc13fee6SSepherosa Ziehau 	 */
985dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_pkts < 0)
986dc13fee6SSepherosa Ziehau 		pkts = UINT32_MAX;
987dc13fee6SSepherosa Ziehau 	else
988dc13fee6SSepherosa Ziehau 		pkts = sc->hn_agg_pkts;
989dc13fee6SSepherosa Ziehau 
990dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_pkts < pkts)
991dc13fee6SSepherosa Ziehau 		pkts = sc->hn_rndis_agg_pkts;
992dc13fee6SSepherosa Ziehau 
993dc13fee6SSepherosa Ziehau 	if (pkts <= 1) {
994dc13fee6SSepherosa Ziehau 		/* Disable */
995dc13fee6SSepherosa Ziehau 		size = 0;
996dc13fee6SSepherosa Ziehau 		pkts = 0;
997dc13fee6SSepherosa Ziehau 		goto done;
998dc13fee6SSepherosa Ziehau 	}
999dc13fee6SSepherosa Ziehau 
1000dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
1001dc13fee6SSepherosa Ziehau 	if (pkts > SHRT_MAX)
1002dc13fee6SSepherosa Ziehau 		pkts = SHRT_MAX;
1003dc13fee6SSepherosa Ziehau 
1004dc13fee6SSepherosa Ziehau done:
1005dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
1006dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_align > SHRT_MAX) {
1007dc13fee6SSepherosa Ziehau 		/* Disable */
1008dc13fee6SSepherosa Ziehau 		size = 0;
1009dc13fee6SSepherosa Ziehau 		pkts = 0;
1010dc13fee6SSepherosa Ziehau 	}
1011dc13fee6SSepherosa Ziehau 
1012dc13fee6SSepherosa Ziehau 	if (bootverbose) {
1013dc13fee6SSepherosa Ziehau 		if_printf(sc->hn_ifp, "TX agg size %u, pkts %u, align %u\n",
1014dc13fee6SSepherosa Ziehau 		    size, pkts, sc->hn_rndis_agg_align);
1015dc13fee6SSepherosa Ziehau 	}
1016dc13fee6SSepherosa Ziehau 
1017dc13fee6SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
1018dc13fee6SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
1019dc13fee6SSepherosa Ziehau 
1020dc13fee6SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
1021dc13fee6SSepherosa Ziehau 		txr->hn_agg_szmax = size;
1022dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktmax = pkts;
1023dc13fee6SSepherosa Ziehau 		txr->hn_agg_align = sc->hn_rndis_agg_align;
1024dc13fee6SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
1025dc13fee6SSepherosa Ziehau 	}
1026dc13fee6SSepherosa Ziehau }
1027dc13fee6SSepherosa Ziehau 
102815516c77SSepherosa Ziehau static int
102915516c77SSepherosa Ziehau hn_get_txswq_depth(const struct hn_tx_ring *txr)
103015516c77SSepherosa Ziehau {
103115516c77SSepherosa Ziehau 
103215516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_cnt > 0, ("tx ring is not setup yet"));
103315516c77SSepherosa Ziehau 	if (hn_tx_swq_depth < txr->hn_txdesc_cnt)
103415516c77SSepherosa Ziehau 		return txr->hn_txdesc_cnt;
103515516c77SSepherosa Ziehau 	return hn_tx_swq_depth;
103615516c77SSepherosa Ziehau }
103715516c77SSepherosa Ziehau 
103815516c77SSepherosa Ziehau static int
103915516c77SSepherosa Ziehau hn_rss_reconfig(struct hn_softc *sc)
104015516c77SSepherosa Ziehau {
104115516c77SSepherosa Ziehau 	int error;
104215516c77SSepherosa Ziehau 
104315516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
104415516c77SSepherosa Ziehau 
104515516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
104615516c77SSepherosa Ziehau 		return (ENXIO);
104715516c77SSepherosa Ziehau 
104815516c77SSepherosa Ziehau 	/*
104915516c77SSepherosa Ziehau 	 * Disable RSS first.
105015516c77SSepherosa Ziehau 	 *
105115516c77SSepherosa Ziehau 	 * NOTE:
105215516c77SSepherosa Ziehau 	 * Direct reconfiguration by setting the UNCHG flags does
105315516c77SSepherosa Ziehau 	 * _not_ work properly.
105415516c77SSepherosa Ziehau 	 */
105515516c77SSepherosa Ziehau 	if (bootverbose)
105615516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "disable RSS\n");
105715516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_DISABLE);
105815516c77SSepherosa Ziehau 	if (error) {
105915516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS disable failed\n");
106015516c77SSepherosa Ziehau 		return (error);
106115516c77SSepherosa Ziehau 	}
106215516c77SSepherosa Ziehau 
106315516c77SSepherosa Ziehau 	/*
106415516c77SSepherosa Ziehau 	 * Reenable the RSS w/ the updated RSS key or indirect
106515516c77SSepherosa Ziehau 	 * table.
106615516c77SSepherosa Ziehau 	 */
106715516c77SSepherosa Ziehau 	if (bootverbose)
106815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "reconfig RSS\n");
106915516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
107015516c77SSepherosa Ziehau 	if (error) {
107115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS reconfig failed\n");
107215516c77SSepherosa Ziehau 		return (error);
107315516c77SSepherosa Ziehau 	}
107415516c77SSepherosa Ziehau 	return (0);
107515516c77SSepherosa Ziehau }
107615516c77SSepherosa Ziehau 
107715516c77SSepherosa Ziehau static void
1078afd4971bSSepherosa Ziehau hn_rss_ind_fixup(struct hn_softc *sc)
107915516c77SSepherosa Ziehau {
108015516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
1081afd4971bSSepherosa Ziehau 	int i, nchan;
108215516c77SSepherosa Ziehau 
1083afd4971bSSepherosa Ziehau 	nchan = sc->hn_rx_ring_inuse;
108415516c77SSepherosa Ziehau 	KASSERT(nchan > 1, ("invalid # of channels %d", nchan));
108515516c77SSepherosa Ziehau 
108615516c77SSepherosa Ziehau 	/*
108715516c77SSepherosa Ziehau 	 * Check indirect table to make sure that all channels in it
108815516c77SSepherosa Ziehau 	 * can be used.
108915516c77SSepherosa Ziehau 	 */
109015516c77SSepherosa Ziehau 	for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
109115516c77SSepherosa Ziehau 		if (rss->rss_ind[i] >= nchan) {
109215516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp,
109315516c77SSepherosa Ziehau 			    "RSS indirect table %d fixup: %u -> %d\n",
109415516c77SSepherosa Ziehau 			    i, rss->rss_ind[i], nchan - 1);
109515516c77SSepherosa Ziehau 			rss->rss_ind[i] = nchan - 1;
109615516c77SSepherosa Ziehau 		}
109715516c77SSepherosa Ziehau 	}
109815516c77SSepherosa Ziehau }
109915516c77SSepherosa Ziehau 
110015516c77SSepherosa Ziehau static int
1101*4db5958aSJustin Hibbits hn_ifmedia_upd(if_t ifp __unused)
110215516c77SSepherosa Ziehau {
110315516c77SSepherosa Ziehau 
110415516c77SSepherosa Ziehau 	return EOPNOTSUPP;
110515516c77SSepherosa Ziehau }
110615516c77SSepherosa Ziehau 
110715516c77SSepherosa Ziehau static void
1108*4db5958aSJustin Hibbits hn_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
110915516c77SSepherosa Ziehau {
1110*4db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
111115516c77SSepherosa Ziehau 
111215516c77SSepherosa Ziehau 	ifmr->ifm_status = IFM_AVALID;
111315516c77SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
111415516c77SSepherosa Ziehau 
111515516c77SSepherosa Ziehau 	if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) {
111615516c77SSepherosa Ziehau 		ifmr->ifm_active |= IFM_NONE;
111715516c77SSepherosa Ziehau 		return;
111815516c77SSepherosa Ziehau 	}
111915516c77SSepherosa Ziehau 	ifmr->ifm_status |= IFM_ACTIVE;
112015516c77SSepherosa Ziehau 	ifmr->ifm_active |= IFM_10G_T | IFM_FDX;
112115516c77SSepherosa Ziehau }
112215516c77SSepherosa Ziehau 
11235bdfd3fdSDexuan Cui static void
1124962f0357SSepherosa Ziehau hn_rxvf_set_task(void *xarg, int pending __unused)
11255bdfd3fdSDexuan Cui {
1126962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg *arg = xarg;
11275bdfd3fdSDexuan Cui 
1128962f0357SSepherosa Ziehau 	arg->rxr->hn_rxvf_ifp = arg->vf_ifp;
11295bdfd3fdSDexuan Cui }
11305bdfd3fdSDexuan Cui 
11315bdfd3fdSDexuan Cui static void
1132*4db5958aSJustin Hibbits hn_rxvf_set(struct hn_softc *sc, if_t vf_ifp)
11335bdfd3fdSDexuan Cui {
11345bdfd3fdSDexuan Cui 	struct hn_rx_ring *rxr;
1135962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg arg;
11365bdfd3fdSDexuan Cui 	struct task task;
11375bdfd3fdSDexuan Cui 	int i;
11385bdfd3fdSDexuan Cui 
11395bdfd3fdSDexuan Cui 	HN_LOCK_ASSERT(sc);
11405bdfd3fdSDexuan Cui 
1141962f0357SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_rxvf_set_task, &arg);
11425bdfd3fdSDexuan Cui 
11435bdfd3fdSDexuan Cui 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
11445bdfd3fdSDexuan Cui 		rxr = &sc->hn_rx_ring[i];
11455bdfd3fdSDexuan Cui 
11465bdfd3fdSDexuan Cui 		if (i < sc->hn_rx_ring_inuse) {
1147962f0357SSepherosa Ziehau 			arg.rxr = rxr;
1148962f0357SSepherosa Ziehau 			arg.vf_ifp = vf_ifp;
11495bdfd3fdSDexuan Cui 			vmbus_chan_run_task(rxr->hn_chan, &task);
11505bdfd3fdSDexuan Cui 		} else {
1151962f0357SSepherosa Ziehau 			rxr->hn_rxvf_ifp = vf_ifp;
11525bdfd3fdSDexuan Cui 		}
11535bdfd3fdSDexuan Cui 	}
11545bdfd3fdSDexuan Cui }
11555bdfd3fdSDexuan Cui 
1156962f0357SSepherosa Ziehau static bool
1157*4db5958aSJustin Hibbits hn_ismyvf(const struct hn_softc *sc, const if_t ifp)
1158499c3e17SSepherosa Ziehau {
1159*4db5958aSJustin Hibbits 	if_t hn_ifp;
1160499c3e17SSepherosa Ziehau 
1161499c3e17SSepherosa Ziehau 	hn_ifp = sc->hn_ifp;
1162499c3e17SSepherosa Ziehau 
1163499c3e17SSepherosa Ziehau 	if (ifp == hn_ifp)
1164499c3e17SSepherosa Ziehau 		return (false);
1165499c3e17SSepherosa Ziehau 
1166*4db5958aSJustin Hibbits 	if (if_getalloctype(ifp) != IFT_ETHER)
1167499c3e17SSepherosa Ziehau 		return (false);
1168499c3e17SSepherosa Ziehau 
1169499c3e17SSepherosa Ziehau 	/* Ignore lagg/vlan interfaces */
1170*4db5958aSJustin Hibbits 	if (strcmp(if_getdname(ifp), "lagg") == 0 ||
1171*4db5958aSJustin Hibbits 	    strcmp(if_getdname(ifp), "vlan") == 0)
1172499c3e17SSepherosa Ziehau 		return (false);
1173499c3e17SSepherosa Ziehau 
1174d76fb49fSDexuan Cui 	/*
1175*4db5958aSJustin Hibbits 	 * During detach events if_getifaddr(ifp) might be NULL.
1176d76fb49fSDexuan Cui 	 * Make sure the bcmp() below doesn't panic on that:
1177d76fb49fSDexuan Cui 	 */
1178*4db5958aSJustin Hibbits 	if (if_getifaddr(ifp) == NULL || if_getifaddr(hn_ifp) == NULL)
1179d76fb49fSDexuan Cui 		return (false);
1180d76fb49fSDexuan Cui 
1181*4db5958aSJustin Hibbits 	if (bcmp(if_getlladdr(ifp), if_getlladdr(hn_ifp), ETHER_ADDR_LEN) != 0)
1182499c3e17SSepherosa Ziehau 		return (false);
1183499c3e17SSepherosa Ziehau 
1184499c3e17SSepherosa Ziehau 	return (true);
1185499c3e17SSepherosa Ziehau }
1186499c3e17SSepherosa Ziehau 
11875bdfd3fdSDexuan Cui static void
1188*4db5958aSJustin Hibbits hn_rxvf_change(struct hn_softc *sc, if_t ifp, bool rxvf)
11895bdfd3fdSDexuan Cui {
1190*4db5958aSJustin Hibbits 	if_t hn_ifp;
11915bdfd3fdSDexuan Cui 
11925bdfd3fdSDexuan Cui 	HN_LOCK(sc);
11935bdfd3fdSDexuan Cui 
11945bdfd3fdSDexuan Cui 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
11955bdfd3fdSDexuan Cui 		goto out;
11965bdfd3fdSDexuan Cui 
1197499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1198499c3e17SSepherosa Ziehau 		goto out;
11995bdfd3fdSDexuan Cui 	hn_ifp = sc->hn_ifp;
12005bdfd3fdSDexuan Cui 
1201962f0357SSepherosa Ziehau 	if (rxvf) {
1202962f0357SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_RXVF)
12035bdfd3fdSDexuan Cui 			goto out;
12045bdfd3fdSDexuan Cui 
1205962f0357SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_RXVF;
12065bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
12075bdfd3fdSDexuan Cui 	} else {
1208962f0357SSepherosa Ziehau 		if (!(sc->hn_flags & HN_FLAG_RXVF))
12095bdfd3fdSDexuan Cui 			goto out;
12105bdfd3fdSDexuan Cui 
1211962f0357SSepherosa Ziehau 		sc->hn_flags &= ~HN_FLAG_RXVF;
1212*4db5958aSJustin Hibbits 		if (if_getdrvflags(hn_ifp) & IFF_DRV_RUNNING)
12135bdfd3fdSDexuan Cui 			hn_rxfilter_config(sc);
12145bdfd3fdSDexuan Cui 		else
12155bdfd3fdSDexuan Cui 			hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE);
12165bdfd3fdSDexuan Cui 	}
12175bdfd3fdSDexuan Cui 
12185bdfd3fdSDexuan Cui 	hn_nvs_set_datapath(sc,
12199c6cae24SSepherosa Ziehau 	    rxvf ? HN_NVS_DATAPATH_VF : HN_NVS_DATAPATH_SYNTH);
12205bdfd3fdSDexuan Cui 
1221962f0357SSepherosa Ziehau 	hn_rxvf_set(sc, rxvf ? ifp : NULL);
12225bdfd3fdSDexuan Cui 
1223962f0357SSepherosa Ziehau 	if (rxvf) {
1224642ec226SSepherosa Ziehau 		hn_vf_rss_fixup(sc, true);
12255bdfd3fdSDexuan Cui 		hn_suspend_mgmt(sc);
12265bdfd3fdSDexuan Cui 		sc->hn_link_flags &=
12275bdfd3fdSDexuan Cui 		    ~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG);
1228499c3e17SSepherosa Ziehau 		if_link_state_change(hn_ifp, LINK_STATE_DOWN);
12295bdfd3fdSDexuan Cui 	} else {
1230642ec226SSepherosa Ziehau 		hn_vf_rss_restore(sc);
12315bdfd3fdSDexuan Cui 		hn_resume_mgmt(sc);
12325bdfd3fdSDexuan Cui 	}
12335bdfd3fdSDexuan Cui 
1234*4db5958aSJustin Hibbits 	devctl_notify("HYPERV_NIC_VF", if_name(hn_ifp),
1235962f0357SSepherosa Ziehau 	    rxvf ? "VF_UP" : "VF_DOWN", NULL);
123633408a34SDexuan Cui 
1237962f0357SSepherosa Ziehau 	if (bootverbose) {
1238962f0357SSepherosa Ziehau 		if_printf(hn_ifp, "datapath is switched %s %s\n",
1239*4db5958aSJustin Hibbits 		    rxvf ? "to" : "from", if_name(ifp));
1240962f0357SSepherosa Ziehau 	}
12415bdfd3fdSDexuan Cui out:
12425bdfd3fdSDexuan Cui 	HN_UNLOCK(sc);
12435bdfd3fdSDexuan Cui }
12445bdfd3fdSDexuan Cui 
12455bdfd3fdSDexuan Cui static void
1246*4db5958aSJustin Hibbits hn_ifnet_event(void *arg, if_t ifp, int event)
12475bdfd3fdSDexuan Cui {
1248962f0357SSepherosa Ziehau 
12495bdfd3fdSDexuan Cui 	if (event != IFNET_EVENT_UP && event != IFNET_EVENT_DOWN)
12505bdfd3fdSDexuan Cui 		return;
1251962f0357SSepherosa Ziehau 	hn_rxvf_change(arg, ifp, event == IFNET_EVENT_UP);
12525bdfd3fdSDexuan Cui }
12535bdfd3fdSDexuan Cui 
12545bdfd3fdSDexuan Cui static void
1255*4db5958aSJustin Hibbits hn_ifaddr_event(void *arg, if_t ifp)
12565bdfd3fdSDexuan Cui {
1257962f0357SSepherosa Ziehau 
1258*4db5958aSJustin Hibbits 	hn_rxvf_change(arg, ifp, if_getflags(ifp) & IFF_UP);
12595bdfd3fdSDexuan Cui }
12605bdfd3fdSDexuan Cui 
12619c6cae24SSepherosa Ziehau static int
12629c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetcaps(struct hn_softc *sc, struct ifreq *ifr)
12639c6cae24SSepherosa Ziehau {
1264*4db5958aSJustin Hibbits 	if_t ifp, vf_ifp;
12659c6cae24SSepherosa Ziehau 	uint64_t tmp;
12669c6cae24SSepherosa Ziehau 	int error;
12679c6cae24SSepherosa Ziehau 
12689c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
12699c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
12709c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
12719c6cae24SSepherosa Ziehau 
12729c6cae24SSepherosa Ziehau 	/*
12739c6cae24SSepherosa Ziehau 	 * Fix up requested capabilities w/ supported capabilities,
12749c6cae24SSepherosa Ziehau 	 * since the supported capabilities could have been changed.
12759c6cae24SSepherosa Ziehau 	 */
1276*4db5958aSJustin Hibbits 	ifr->ifr_reqcap &= if_getcapabilities(ifp);
12779c6cae24SSepherosa Ziehau 	/* Pass SIOCSIFCAP to VF. */
1278*4db5958aSJustin Hibbits 	error = ifhwioctl(SIOCSIFCAP, vf_ifp, (caddr_t)ifr, curthread);
12799c6cae24SSepherosa Ziehau 
12809c6cae24SSepherosa Ziehau 	/*
12819c6cae24SSepherosa Ziehau 	 * NOTE:
12829c6cae24SSepherosa Ziehau 	 * The error will be propagated to the callers, however, it
12839c6cae24SSepherosa Ziehau 	 * is _not_ useful here.
12849c6cae24SSepherosa Ziehau 	 */
12859c6cae24SSepherosa Ziehau 
12869c6cae24SSepherosa Ziehau 	/*
12879c6cae24SSepherosa Ziehau 	 * Merge VF's enabled capabilities.
12889c6cae24SSepherosa Ziehau 	 */
1289*4db5958aSJustin Hibbits 	if_setcapenable(ifp, if_getcapenable(vf_ifp) & if_getcapabilities(ifp));
12909c6cae24SSepherosa Ziehau 
1291*4db5958aSJustin Hibbits 	tmp = if_gethwassist(vf_ifp) & HN_CSUM_IP_HWASSIST(sc);
1292*4db5958aSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_TXCSUM)
1293*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, tmp, 0);
12949c6cae24SSepherosa Ziehau 	else
1295*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, 0, tmp);
12969c6cae24SSepherosa Ziehau 
1297*4db5958aSJustin Hibbits 	tmp = if_gethwassist(vf_ifp) & HN_CSUM_IP6_HWASSIST(sc);
1298*4db5958aSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6)
1299*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, tmp, 0);
13009c6cae24SSepherosa Ziehau 	else
1301*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, 0, tmp);
13029c6cae24SSepherosa Ziehau 
1303*4db5958aSJustin Hibbits 	tmp = if_gethwassist(vf_ifp) & CSUM_IP_TSO;
1304*4db5958aSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_TSO4)
1305*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, tmp, 0);
13069c6cae24SSepherosa Ziehau 	else
1307*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, 0, tmp);
13089c6cae24SSepherosa Ziehau 
1309*4db5958aSJustin Hibbits 	tmp = if_gethwassist(vf_ifp) & CSUM_IP6_TSO;
1310*4db5958aSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_TSO6)
1311*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, tmp, 0);
13129c6cae24SSepherosa Ziehau 	else
1313*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, 0, tmp);
13149c6cae24SSepherosa Ziehau 
13159c6cae24SSepherosa Ziehau 	return (error);
13169c6cae24SSepherosa Ziehau }
13179c6cae24SSepherosa Ziehau 
13189c6cae24SSepherosa Ziehau static int
13199c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetflags(struct hn_softc *sc)
13209c6cae24SSepherosa Ziehau {
1321*4db5958aSJustin Hibbits 	if_t vf_ifp;
13229c6cae24SSepherosa Ziehau 	struct ifreq ifr;
13239c6cae24SSepherosa Ziehau 
13249c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13259c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
13269c6cae24SSepherosa Ziehau 
13279c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
1328*4db5958aSJustin Hibbits 	strlcpy(ifr.ifr_name, if_name(vf_ifp), sizeof(ifr.ifr_name));
1329*4db5958aSJustin Hibbits 	ifr.ifr_flags = if_getflags(vf_ifp) & 0xffff;
1330*4db5958aSJustin Hibbits 	ifr.ifr_flagshigh = if_getflags(vf_ifp) >> 16;
1331*4db5958aSJustin Hibbits 	return (ifhwioctl(SIOCSIFFLAGS, vf_ifp, (caddr_t)&ifr, curthread));
13329c6cae24SSepherosa Ziehau }
13339c6cae24SSepherosa Ziehau 
13349c6cae24SSepherosa Ziehau static void
13359c6cae24SSepherosa Ziehau hn_xpnt_vf_saveifflags(struct hn_softc *sc)
13369c6cae24SSepherosa Ziehau {
1337*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
13389c6cae24SSepherosa Ziehau 	int allmulti = 0;
13399c6cae24SSepherosa Ziehau 
13409c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13419c6cae24SSepherosa Ziehau 
13429c6cae24SSepherosa Ziehau 	/* XXX vlan(4) style mcast addr maintenance */
1343*4db5958aSJustin Hibbits 	if (!if_maddr_empty(ifp))
13449c6cae24SSepherosa Ziehau 		allmulti = IFF_ALLMULTI;
13459c6cae24SSepherosa Ziehau 
13469c6cae24SSepherosa Ziehau 	/* Always set the VF's if_flags */
1347*4db5958aSJustin Hibbits 	if_setflags(sc->hn_vf_ifp, if_getflags(ifp) | allmulti);
13489c6cae24SSepherosa Ziehau }
13499c6cae24SSepherosa Ziehau 
13509c6cae24SSepherosa Ziehau static void
1351*4db5958aSJustin Hibbits hn_xpnt_vf_input(if_t vf_ifp, struct mbuf *m)
13529c6cae24SSepherosa Ziehau {
13539c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
1354*4db5958aSJustin Hibbits 	if_t hn_ifp = NULL;
13559c6cae24SSepherosa Ziehau 	struct mbuf *mn;
13569c6cae24SSepherosa Ziehau 
13579c6cae24SSepherosa Ziehau 	/*
13589c6cae24SSepherosa Ziehau 	 * XXX racy, if hn(4) ever detached.
13599c6cae24SSepherosa Ziehau 	 */
13609c6cae24SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
1361*4db5958aSJustin Hibbits 	if (if_getindex(vf_ifp) < hn_vfmap_size)
1362*4db5958aSJustin Hibbits 		hn_ifp = hn_vfmap[if_getindex(vf_ifp)];
13639c6cae24SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
13649c6cae24SSepherosa Ziehau 
13659c6cae24SSepherosa Ziehau 	if (hn_ifp != NULL) {
13669c6cae24SSepherosa Ziehau 		for (mn = m; mn != NULL; mn = mn->m_nextpkt) {
13673bed4e54SSepherosa Ziehau 			/*
13683bed4e54SSepherosa Ziehau 			 * Allow tapping on the VF.
13693bed4e54SSepherosa Ziehau 			 */
13709c6cae24SSepherosa Ziehau 			ETHER_BPF_MTAP(vf_ifp, mn);
13713bed4e54SSepherosa Ziehau 
13723bed4e54SSepherosa Ziehau 			/*
13733bed4e54SSepherosa Ziehau 			 * Update VF stats.
13743bed4e54SSepherosa Ziehau 			 */
1375*4db5958aSJustin Hibbits 			if ((if_getcapenable(vf_ifp) & IFCAP_HWSTATS) == 0) {
13763bed4e54SSepherosa Ziehau 				if_inc_counter(vf_ifp, IFCOUNTER_IBYTES,
13773bed4e54SSepherosa Ziehau 				    mn->m_pkthdr.len);
13783bed4e54SSepherosa Ziehau 			}
13793bed4e54SSepherosa Ziehau 			/*
13803bed4e54SSepherosa Ziehau 			 * XXX IFCOUNTER_IMCAST
13813bed4e54SSepherosa Ziehau 			 * This stat updating is kinda invasive, since it
13823bed4e54SSepherosa Ziehau 			 * requires two checks on the mbuf: the length check
13833bed4e54SSepherosa Ziehau 			 * and the ethernet header check.  As of this write,
13843bed4e54SSepherosa Ziehau 			 * all multicast packets go directly to hn(4), which
13853bed4e54SSepherosa Ziehau 			 * makes imcast stat updating in the VF a try in vian.
13863bed4e54SSepherosa Ziehau 			 */
13873bed4e54SSepherosa Ziehau 
13883bed4e54SSepherosa Ziehau 			/*
13893bed4e54SSepherosa Ziehau 			 * Fix up rcvif and increase hn(4)'s ipackets.
13903bed4e54SSepherosa Ziehau 			 */
13919c6cae24SSepherosa Ziehau 			mn->m_pkthdr.rcvif = hn_ifp;
13929c6cae24SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
13939c6cae24SSepherosa Ziehau 		}
13943bed4e54SSepherosa Ziehau 		/*
13953bed4e54SSepherosa Ziehau 		 * Go through hn(4)'s if_input.
13963bed4e54SSepherosa Ziehau 		 */
1397*4db5958aSJustin Hibbits 		if_input(hn_ifp, m);
13989c6cae24SSepherosa Ziehau 	} else {
13999c6cae24SSepherosa Ziehau 		/*
14009c6cae24SSepherosa Ziehau 		 * In the middle of the transition; free this
14019c6cae24SSepherosa Ziehau 		 * mbuf chain.
14029c6cae24SSepherosa Ziehau 		 */
14039c6cae24SSepherosa Ziehau 		while (m != NULL) {
14049c6cae24SSepherosa Ziehau 			mn = m->m_nextpkt;
14059c6cae24SSepherosa Ziehau 			m->m_nextpkt = NULL;
14069c6cae24SSepherosa Ziehau 			m_freem(m);
14079c6cae24SSepherosa Ziehau 			m = mn;
14089c6cae24SSepherosa Ziehau 		}
14099c6cae24SSepherosa Ziehau 	}
14109c6cae24SSepherosa Ziehau }
14119c6cae24SSepherosa Ziehau 
14129c6cae24SSepherosa Ziehau static void
14139c6cae24SSepherosa Ziehau hn_mtu_change_fixup(struct hn_softc *sc)
14149c6cae24SSepherosa Ziehau {
1415*4db5958aSJustin Hibbits 	if_t ifp;
14169c6cae24SSepherosa Ziehau 
14179c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
14189c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
14199c6cae24SSepherosa Ziehau 
1420*4db5958aSJustin Hibbits 	hn_set_tso_maxsize(sc, hn_tso_maxlen, if_getmtu(ifp));
14219c6cae24SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_lro.lro_length_lim < HN_LRO_LENLIM_MIN(ifp))
14229c6cae24SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp));
14239c6cae24SSepherosa Ziehau }
14249c6cae24SSepherosa Ziehau 
1425642ec226SSepherosa Ziehau static uint32_t
1426642ec226SSepherosa Ziehau hn_rss_type_fromndis(uint32_t rss_hash)
1427642ec226SSepherosa Ziehau {
1428642ec226SSepherosa Ziehau 	uint32_t types = 0;
1429642ec226SSepherosa Ziehau 
1430642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV4)
1431642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV4;
1432642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV4)
1433642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV4;
1434642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV6)
1435642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV6;
1436642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV6_EX)
1437642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV6_EX;
1438642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV6)
1439642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV6;
1440642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV6_EX)
1441642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV6_EX;
14426f12c42eSSepherosa Ziehau 	if (rss_hash & NDIS_HASH_UDP_IPV4_X)
14436f12c42eSSepherosa Ziehau 		types |= RSS_TYPE_UDP_IPV4;
1444642ec226SSepherosa Ziehau 	return (types);
1445642ec226SSepherosa Ziehau }
1446642ec226SSepherosa Ziehau 
1447642ec226SSepherosa Ziehau static uint32_t
1448642ec226SSepherosa Ziehau hn_rss_type_tondis(uint32_t types)
1449642ec226SSepherosa Ziehau {
1450642ec226SSepherosa Ziehau 	uint32_t rss_hash = 0;
1451642ec226SSepherosa Ziehau 
14526f12c42eSSepherosa Ziehau 	KASSERT((types & (RSS_TYPE_UDP_IPV6 | RSS_TYPE_UDP_IPV6_EX)) == 0,
14536f12c42eSSepherosa Ziehau 	    ("UDP6 and UDP6EX are not supported"));
1454642ec226SSepherosa Ziehau 
1455642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV4)
1456642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV4;
1457642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV4)
1458642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV4;
1459642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV6)
1460642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV6;
1461642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV6_EX)
1462642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV6_EX;
1463642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV6)
1464642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV6;
1465642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV6_EX)
1466642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV6_EX;
14676f12c42eSSepherosa Ziehau 	if (types & RSS_TYPE_UDP_IPV4)
14686f12c42eSSepherosa Ziehau 		rss_hash |= NDIS_HASH_UDP_IPV4_X;
1469642ec226SSepherosa Ziehau 	return (rss_hash);
1470642ec226SSepherosa Ziehau }
1471642ec226SSepherosa Ziehau 
1472642ec226SSepherosa Ziehau static void
1473642ec226SSepherosa Ziehau hn_rss_mbuf_hash(struct hn_softc *sc, uint32_t mbuf_hash)
1474642ec226SSepherosa Ziehau {
1475642ec226SSepherosa Ziehau 	int i;
1476642ec226SSepherosa Ziehau 
1477642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1478642ec226SSepherosa Ziehau 
1479642ec226SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1480642ec226SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_mbuf_hash = mbuf_hash;
1481642ec226SSepherosa Ziehau }
1482642ec226SSepherosa Ziehau 
1483642ec226SSepherosa Ziehau static void
1484642ec226SSepherosa Ziehau hn_vf_rss_fixup(struct hn_softc *sc, bool reconf)
1485642ec226SSepherosa Ziehau {
1486*4db5958aSJustin Hibbits 	if_t ifp, vf_ifp;
1487642ec226SSepherosa Ziehau 	struct ifrsshash ifrh;
1488642ec226SSepherosa Ziehau 	struct ifrsskey ifrk;
1489642ec226SSepherosa Ziehau 	int error;
1490642ec226SSepherosa Ziehau 	uint32_t my_types, diff_types, mbuf_types = 0;
1491642ec226SSepherosa Ziehau 
1492642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1493642ec226SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
1494*4db5958aSJustin Hibbits 	    ("%s: synthetic parts are not attached", if_name(sc->hn_ifp)));
1495642ec226SSepherosa Ziehau 
1496642ec226SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
1497642ec226SSepherosa Ziehau 		/* No RSS on synthetic parts; done. */
1498642ec226SSepherosa Ziehau 		return;
1499642ec226SSepherosa Ziehau 	}
1500642ec226SSepherosa Ziehau 	if ((sc->hn_rss_hcap & NDIS_HASH_FUNCTION_TOEPLITZ) == 0) {
1501642ec226SSepherosa Ziehau 		/* Synthetic parts do not support Toeplitz; done. */
1502642ec226SSepherosa Ziehau 		return;
1503642ec226SSepherosa Ziehau 	}
1504642ec226SSepherosa Ziehau 
1505642ec226SSepherosa Ziehau 	ifp = sc->hn_ifp;
1506642ec226SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
1507642ec226SSepherosa Ziehau 
1508642ec226SSepherosa Ziehau 	/*
1509642ec226SSepherosa Ziehau 	 * Extract VF's RSS key.  Only 40 bytes key for Toeplitz is
1510642ec226SSepherosa Ziehau 	 * supported.
1511642ec226SSepherosa Ziehau 	 */
1512642ec226SSepherosa Ziehau 	memset(&ifrk, 0, sizeof(ifrk));
1513*4db5958aSJustin Hibbits 	strlcpy(ifrk.ifrk_name, if_name(vf_ifp), sizeof(ifrk.ifrk_name));
1514*4db5958aSJustin Hibbits 	error = ifhwioctl(SIOCGIFRSSKEY, vf_ifp, (caddr_t)&ifrk, curthread);
1515642ec226SSepherosa Ziehau 	if (error) {
1516a3b413afSHans Petter Selasky 		if_printf(ifp, "%s SIOCGIFRSSKEY failed: %d\n",
1517*4db5958aSJustin Hibbits 		    if_name(vf_ifp), error);
1518642ec226SSepherosa Ziehau 		goto done;
1519642ec226SSepherosa Ziehau 	}
1520642ec226SSepherosa Ziehau 	if (ifrk.ifrk_func != RSS_FUNC_TOEPLITZ) {
1521642ec226SSepherosa Ziehau 		if_printf(ifp, "%s RSS function %u is not Toeplitz\n",
1522*4db5958aSJustin Hibbits 		    if_name(vf_ifp), ifrk.ifrk_func);
1523642ec226SSepherosa Ziehau 		goto done;
1524642ec226SSepherosa Ziehau 	}
1525642ec226SSepherosa Ziehau 	if (ifrk.ifrk_keylen != NDIS_HASH_KEYSIZE_TOEPLITZ) {
1526642ec226SSepherosa Ziehau 		if_printf(ifp, "%s invalid RSS Toeplitz key length %d\n",
1527*4db5958aSJustin Hibbits 		    if_name(vf_ifp), ifrk.ifrk_keylen);
1528642ec226SSepherosa Ziehau 		goto done;
1529642ec226SSepherosa Ziehau 	}
1530642ec226SSepherosa Ziehau 
1531642ec226SSepherosa Ziehau 	/*
1532642ec226SSepherosa Ziehau 	 * Extract VF's RSS hash.  Only Toeplitz is supported.
1533642ec226SSepherosa Ziehau 	 */
1534642ec226SSepherosa Ziehau 	memset(&ifrh, 0, sizeof(ifrh));
1535*4db5958aSJustin Hibbits 	strlcpy(ifrh.ifrh_name, if_name(vf_ifp), sizeof(ifrh.ifrh_name));
1536*4db5958aSJustin Hibbits 	error = ifhwioctl(SIOCGIFRSSHASH, vf_ifp, (caddr_t)&ifrh, curthread);
1537642ec226SSepherosa Ziehau 	if (error) {
1538642ec226SSepherosa Ziehau 		if_printf(ifp, "%s SIOCGRSSHASH failed: %d\n",
1539*4db5958aSJustin Hibbits 		    if_name(vf_ifp), error);
1540642ec226SSepherosa Ziehau 		goto done;
1541642ec226SSepherosa Ziehau 	}
1542642ec226SSepherosa Ziehau 	if (ifrh.ifrh_func != RSS_FUNC_TOEPLITZ) {
1543642ec226SSepherosa Ziehau 		if_printf(ifp, "%s RSS function %u is not Toeplitz\n",
1544*4db5958aSJustin Hibbits 		    if_name(vf_ifp), ifrh.ifrh_func);
1545642ec226SSepherosa Ziehau 		goto done;
1546642ec226SSepherosa Ziehau 	}
1547642ec226SSepherosa Ziehau 
1548642ec226SSepherosa Ziehau 	my_types = hn_rss_type_fromndis(sc->hn_rss_hcap);
1549642ec226SSepherosa Ziehau 	if ((ifrh.ifrh_types & my_types) == 0) {
1550642ec226SSepherosa Ziehau 		/* This disables RSS; ignore it then */
1551642ec226SSepherosa Ziehau 		if_printf(ifp, "%s intersection of RSS types failed.  "
1552*4db5958aSJustin Hibbits 		    "VF %#x, mine %#x\n", if_name(vf_ifp),
1553642ec226SSepherosa Ziehau 		    ifrh.ifrh_types, my_types);
1554642ec226SSepherosa Ziehau 		goto done;
1555642ec226SSepherosa Ziehau 	}
1556642ec226SSepherosa Ziehau 
1557642ec226SSepherosa Ziehau 	diff_types = my_types ^ ifrh.ifrh_types;
1558642ec226SSepherosa Ziehau 	my_types &= ifrh.ifrh_types;
1559642ec226SSepherosa Ziehau 	mbuf_types = my_types;
1560642ec226SSepherosa Ziehau 
1561642ec226SSepherosa Ziehau 	/*
1562642ec226SSepherosa Ziehau 	 * Detect RSS hash value/type confliction.
1563642ec226SSepherosa Ziehau 	 *
1564642ec226SSepherosa Ziehau 	 * NOTE:
1565642ec226SSepherosa Ziehau 	 * We don't disable the hash type, but stop delivery the hash
1566642ec226SSepherosa Ziehau 	 * value/type through mbufs on RX path.
15676f12c42eSSepherosa Ziehau 	 *
15686f12c42eSSepherosa Ziehau 	 * XXX If HN_CAP_UDPHASH is set in hn_caps, then UDP 4-tuple
15696f12c42eSSepherosa Ziehau 	 * hash is delivered with type of TCP_IPV4.  This means if
15706f12c42eSSepherosa Ziehau 	 * UDP_IPV4 is enabled, then TCP_IPV4 should be forced, at
15716f12c42eSSepherosa Ziehau 	 * least to hn_mbuf_hash.  However, given that _all_ of the
15726f12c42eSSepherosa Ziehau 	 * NICs implement TCP_IPV4, this will _not_ impose any issues
15736f12c42eSSepherosa Ziehau 	 * here.
1574642ec226SSepherosa Ziehau 	 */
1575642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV4) &&
1576642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1577642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV4 | RSS_TYPE_UDP_IPV4))) {
1578642ec226SSepherosa Ziehau 		/* Conflict; disable IPV4 hash type/value delivery. */
1579642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV4 mbuf hash delivery\n");
1580642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV4;
1581642ec226SSepherosa Ziehau 	}
1582642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV6) &&
1583642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1584642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV6 | RSS_TYPE_UDP_IPV6 |
1585642ec226SSepherosa Ziehau 	      RSS_TYPE_TCP_IPV6_EX | RSS_TYPE_UDP_IPV6_EX |
1586642ec226SSepherosa Ziehau 	      RSS_TYPE_IPV6_EX))) {
1587642ec226SSepherosa Ziehau 		/* Conflict; disable IPV6 hash type/value delivery. */
1588642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV6 mbuf hash delivery\n");
1589642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV6;
1590642ec226SSepherosa Ziehau 	}
1591642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV6_EX) &&
1592642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1593642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV6 | RSS_TYPE_UDP_IPV6 |
1594642ec226SSepherosa Ziehau 	      RSS_TYPE_TCP_IPV6_EX | RSS_TYPE_UDP_IPV6_EX |
1595642ec226SSepherosa Ziehau 	      RSS_TYPE_IPV6))) {
1596642ec226SSepherosa Ziehau 		/* Conflict; disable IPV6_EX hash type/value delivery. */
1597642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV6_EX mbuf hash delivery\n");
1598642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV6_EX;
1599642ec226SSepherosa Ziehau 	}
1600642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_TCP_IPV6) &&
1601642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_TCP_IPV6_EX)) {
1602642ec226SSepherosa Ziehau 		/* Conflict; disable TCP_IPV6 hash type/value delivery. */
1603642ec226SSepherosa Ziehau 		if_printf(ifp, "disable TCP_IPV6 mbuf hash delivery\n");
1604642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_TCP_IPV6;
1605642ec226SSepherosa Ziehau 	}
1606642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_TCP_IPV6_EX) &&
1607642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_TCP_IPV6)) {
1608642ec226SSepherosa Ziehau 		/* Conflict; disable TCP_IPV6_EX hash type/value delivery. */
1609642ec226SSepherosa Ziehau 		if_printf(ifp, "disable TCP_IPV6_EX mbuf hash delivery\n");
1610642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_TCP_IPV6_EX;
1611642ec226SSepherosa Ziehau 	}
1612642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_UDP_IPV6) &&
1613642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_UDP_IPV6_EX)) {
1614642ec226SSepherosa Ziehau 		/* Conflict; disable UDP_IPV6 hash type/value delivery. */
1615642ec226SSepherosa Ziehau 		if_printf(ifp, "disable UDP_IPV6 mbuf hash delivery\n");
1616642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_UDP_IPV6;
1617642ec226SSepherosa Ziehau 	}
1618642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_UDP_IPV6_EX) &&
1619642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_UDP_IPV6)) {
1620642ec226SSepherosa Ziehau 		/* Conflict; disable UDP_IPV6_EX hash type/value delivery. */
1621642ec226SSepherosa Ziehau 		if_printf(ifp, "disable UDP_IPV6_EX mbuf hash delivery\n");
1622642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_UDP_IPV6_EX;
1623642ec226SSepherosa Ziehau 	}
1624642ec226SSepherosa Ziehau 
1625642ec226SSepherosa Ziehau 	/*
1626642ec226SSepherosa Ziehau 	 * Indirect table does not matter.
1627642ec226SSepherosa Ziehau 	 */
1628642ec226SSepherosa Ziehau 
1629642ec226SSepherosa Ziehau 	sc->hn_rss_hash = (sc->hn_rss_hcap & NDIS_HASH_FUNCTION_MASK) |
1630642ec226SSepherosa Ziehau 	    hn_rss_type_tondis(my_types);
1631642ec226SSepherosa Ziehau 	memcpy(sc->hn_rss.rss_key, ifrk.ifrk_key, sizeof(sc->hn_rss.rss_key));
1632642ec226SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
1633642ec226SSepherosa Ziehau 
1634642ec226SSepherosa Ziehau 	if (reconf) {
1635642ec226SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
1636642ec226SSepherosa Ziehau 		if (error) {
1637642ec226SSepherosa Ziehau 			/* XXX roll-back? */
1638642ec226SSepherosa Ziehau 			if_printf(ifp, "hn_rss_reconfig failed: %d\n", error);
1639642ec226SSepherosa Ziehau 			/* XXX keep going. */
1640642ec226SSepherosa Ziehau 		}
1641642ec226SSepherosa Ziehau 	}
1642642ec226SSepherosa Ziehau done:
1643642ec226SSepherosa Ziehau 	/* Hash deliverability for mbufs. */
1644642ec226SSepherosa Ziehau 	hn_rss_mbuf_hash(sc, hn_rss_type_tondis(mbuf_types));
1645642ec226SSepherosa Ziehau }
1646642ec226SSepherosa Ziehau 
1647642ec226SSepherosa Ziehau static void
1648642ec226SSepherosa Ziehau hn_vf_rss_restore(struct hn_softc *sc)
1649642ec226SSepherosa Ziehau {
1650642ec226SSepherosa Ziehau 
1651642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1652642ec226SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
1653*4db5958aSJustin Hibbits 	    ("%s: synthetic parts are not attached", if_name(sc->hn_ifp)));
1654642ec226SSepherosa Ziehau 
1655642ec226SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1)
1656642ec226SSepherosa Ziehau 		goto done;
1657642ec226SSepherosa Ziehau 
1658642ec226SSepherosa Ziehau 	/*
1659642ec226SSepherosa Ziehau 	 * Restore hash types.  Key does _not_ matter.
1660642ec226SSepherosa Ziehau 	 */
1661642ec226SSepherosa Ziehau 	if (sc->hn_rss_hash != sc->hn_rss_hcap) {
1662642ec226SSepherosa Ziehau 		int error;
1663642ec226SSepherosa Ziehau 
1664642ec226SSepherosa Ziehau 		sc->hn_rss_hash = sc->hn_rss_hcap;
1665642ec226SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
1666642ec226SSepherosa Ziehau 		if (error) {
1667642ec226SSepherosa Ziehau 			if_printf(sc->hn_ifp, "hn_rss_reconfig failed: %d\n",
1668642ec226SSepherosa Ziehau 			    error);
1669642ec226SSepherosa Ziehau 			/* XXX keep going. */
1670642ec226SSepherosa Ziehau 		}
1671642ec226SSepherosa Ziehau 	}
1672642ec226SSepherosa Ziehau done:
1673642ec226SSepherosa Ziehau 	/* Hash deliverability for mbufs. */
1674642ec226SSepherosa Ziehau 	hn_rss_mbuf_hash(sc, NDIS_HASH_ALL);
1675642ec226SSepherosa Ziehau }
1676642ec226SSepherosa Ziehau 
16779c6cae24SSepherosa Ziehau static void
16789c6cae24SSepherosa Ziehau hn_xpnt_vf_setready(struct hn_softc *sc)
16799c6cae24SSepherosa Ziehau {
1680*4db5958aSJustin Hibbits 	if_t ifp, vf_ifp;
16819c6cae24SSepherosa Ziehau 	struct ifreq ifr;
16829c6cae24SSepherosa Ziehau 
16839c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
16849c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
16859c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
16869c6cae24SSepherosa Ziehau 
16879c6cae24SSepherosa Ziehau 	/*
16889c6cae24SSepherosa Ziehau 	 * Mark the VF ready.
16899c6cae24SSepherosa Ziehau 	 */
16909c6cae24SSepherosa Ziehau 	sc->hn_vf_rdytick = 0;
16919c6cae24SSepherosa Ziehau 
16929c6cae24SSepherosa Ziehau 	/*
16939c6cae24SSepherosa Ziehau 	 * Save information for restoration.
16949c6cae24SSepherosa Ziehau 	 */
1695*4db5958aSJustin Hibbits 	sc->hn_saved_caps = if_getcapabilities(ifp);
1696*4db5958aSJustin Hibbits 	sc->hn_saved_tsomax = if_gethwtsomax(ifp);
1697*4db5958aSJustin Hibbits 	sc->hn_saved_tsosegcnt = if_gethwtsomaxsegcount(ifp);
1698*4db5958aSJustin Hibbits 	sc->hn_saved_tsosegsz = if_gethwtsomaxsegsize(ifp);
16999c6cae24SSepherosa Ziehau 
17009c6cae24SSepherosa Ziehau 	/*
17019c6cae24SSepherosa Ziehau 	 * Intersect supported/enabled capabilities.
17029c6cae24SSepherosa Ziehau 	 *
17039c6cae24SSepherosa Ziehau 	 * NOTE:
17049c6cae24SSepherosa Ziehau 	 * if_hwassist is not changed here.
17059c6cae24SSepherosa Ziehau 	 */
1706*4db5958aSJustin Hibbits 	if_setcapabilitiesbit(ifp, 0, if_getcapabilities(vf_ifp));
1707*4db5958aSJustin Hibbits 	if_setcapenablebit(ifp, 0, if_getcapabilities(ifp));
17089c6cae24SSepherosa Ziehau 
17099c6cae24SSepherosa Ziehau 	/*
17109c6cae24SSepherosa Ziehau 	 * Fix TSO settings.
17119c6cae24SSepherosa Ziehau 	 */
1712*4db5958aSJustin Hibbits 	if (if_gethwtsomax(ifp) > if_gethwtsomax(vf_ifp))
1713*4db5958aSJustin Hibbits 		if_sethwtsomax(ifp, if_gethwtsomax(vf_ifp));
1714*4db5958aSJustin Hibbits 	if (if_gethwtsomaxsegcount(ifp) > if_gethwtsomaxsegcount(vf_ifp))
1715*4db5958aSJustin Hibbits 		if_sethwtsomaxsegcount(ifp, if_gethwtsomaxsegcount(vf_ifp));
1716*4db5958aSJustin Hibbits 	if (if_gethwtsomaxsegsize(ifp) > if_gethwtsomaxsegsize(vf_ifp))
1717*4db5958aSJustin Hibbits 		if_sethwtsomaxsegsize(ifp, if_gethwtsomaxsegsize(vf_ifp));
17189c6cae24SSepherosa Ziehau 
17199c6cae24SSepherosa Ziehau 	/*
17209c6cae24SSepherosa Ziehau 	 * Change VF's enabled capabilities.
17219c6cae24SSepherosa Ziehau 	 */
17229c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
1723*4db5958aSJustin Hibbits 	strlcpy(ifr.ifr_name, if_name(vf_ifp), sizeof(ifr.ifr_name));
1724*4db5958aSJustin Hibbits 	ifr.ifr_reqcap = if_getcapenable(ifp);
17259c6cae24SSepherosa Ziehau 	hn_xpnt_vf_iocsetcaps(sc, &ifr);
17269c6cae24SSepherosa Ziehau 
1727*4db5958aSJustin Hibbits 	if (if_getmtu(ifp) != ETHERMTU) {
17289c6cae24SSepherosa Ziehau 		int error;
17299c6cae24SSepherosa Ziehau 
17309c6cae24SSepherosa Ziehau 		/*
17319c6cae24SSepherosa Ziehau 		 * Change VF's MTU.
17329c6cae24SSepherosa Ziehau 		 */
17339c6cae24SSepherosa Ziehau 		memset(&ifr, 0, sizeof(ifr));
1734*4db5958aSJustin Hibbits 		strlcpy(ifr.ifr_name, if_name(vf_ifp), sizeof(ifr.ifr_name));
1735*4db5958aSJustin Hibbits 		ifr.ifr_mtu = if_getmtu(ifp);
1736*4db5958aSJustin Hibbits 		error = ifhwioctl(SIOCSIFMTU, vf_ifp, (caddr_t)&ifr, curthread);
17379c6cae24SSepherosa Ziehau 		if (error) {
17389c6cae24SSepherosa Ziehau 			if_printf(ifp, "%s SIOCSIFMTU %u failed\n",
1739*4db5958aSJustin Hibbits 			    if_name(vf_ifp), if_getmtu(ifp));
1740*4db5958aSJustin Hibbits 			if (if_getmtu(ifp) > ETHERMTU) {
17419c6cae24SSepherosa Ziehau 				if_printf(ifp, "change MTU to %d\n", ETHERMTU);
17429c6cae24SSepherosa Ziehau 
17439c6cae24SSepherosa Ziehau 				/*
17449c6cae24SSepherosa Ziehau 				 * XXX
17459c6cae24SSepherosa Ziehau 				 * No need to adjust the synthetic parts' MTU;
17469c6cae24SSepherosa Ziehau 				 * failure of the adjustment will cause us
17479c6cae24SSepherosa Ziehau 				 * infinite headache.
17489c6cae24SSepherosa Ziehau 				 */
1749*4db5958aSJustin Hibbits 				if_setmtu(ifp, ETHERMTU);
17509c6cae24SSepherosa Ziehau 				hn_mtu_change_fixup(sc);
17519c6cae24SSepherosa Ziehau 			}
17529c6cae24SSepherosa Ziehau 		}
17539c6cae24SSepherosa Ziehau 	}
17549c6cae24SSepherosa Ziehau }
17559c6cae24SSepherosa Ziehau 
17569c6cae24SSepherosa Ziehau static bool
17579c6cae24SSepherosa Ziehau hn_xpnt_vf_isready(struct hn_softc *sc)
17589c6cae24SSepherosa Ziehau {
17599c6cae24SSepherosa Ziehau 
17609c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
17619c6cae24SSepherosa Ziehau 
17629c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf || sc->hn_vf_ifp == NULL)
17639c6cae24SSepherosa Ziehau 		return (false);
17649c6cae24SSepherosa Ziehau 
17659c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick == 0)
17669c6cae24SSepherosa Ziehau 		return (true);
17679c6cae24SSepherosa Ziehau 
17689c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick > ticks)
17699c6cae24SSepherosa Ziehau 		return (false);
17709c6cae24SSepherosa Ziehau 
17719c6cae24SSepherosa Ziehau 	/* Mark VF as ready. */
17729c6cae24SSepherosa Ziehau 	hn_xpnt_vf_setready(sc);
17739c6cae24SSepherosa Ziehau 	return (true);
17749c6cae24SSepherosa Ziehau }
17759c6cae24SSepherosa Ziehau 
17769c6cae24SSepherosa Ziehau static void
1777a97fff19SSepherosa Ziehau hn_xpnt_vf_setenable(struct hn_softc *sc)
1778a97fff19SSepherosa Ziehau {
1779a97fff19SSepherosa Ziehau 	int i;
1780a97fff19SSepherosa Ziehau 
1781a97fff19SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1782a97fff19SSepherosa Ziehau 
1783a97fff19SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
1784a97fff19SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
1785a97fff19SSepherosa Ziehau 	sc->hn_xvf_flags |= HN_XVFFLAG_ENABLED;
1786a97fff19SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1787a97fff19SSepherosa Ziehau 
1788a97fff19SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1789a97fff19SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_XPNT_VF;
1790a97fff19SSepherosa Ziehau }
1791a97fff19SSepherosa Ziehau 
1792a97fff19SSepherosa Ziehau static void
1793a97fff19SSepherosa Ziehau hn_xpnt_vf_setdisable(struct hn_softc *sc, bool clear_vf)
1794a97fff19SSepherosa Ziehau {
1795a97fff19SSepherosa Ziehau 	int i;
1796a97fff19SSepherosa Ziehau 
1797a97fff19SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1798a97fff19SSepherosa Ziehau 
1799a97fff19SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
1800a97fff19SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
1801a97fff19SSepherosa Ziehau 	sc->hn_xvf_flags &= ~HN_XVFFLAG_ENABLED;
1802a97fff19SSepherosa Ziehau 	if (clear_vf)
1803a97fff19SSepherosa Ziehau 		sc->hn_vf_ifp = NULL;
1804a97fff19SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1805a97fff19SSepherosa Ziehau 
1806a97fff19SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1807a97fff19SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_rx_flags &= ~HN_RX_FLAG_XPNT_VF;
1808a97fff19SSepherosa Ziehau }
1809a97fff19SSepherosa Ziehau 
1810a97fff19SSepherosa Ziehau static void
18119c6cae24SSepherosa Ziehau hn_xpnt_vf_init(struct hn_softc *sc)
18129c6cae24SSepherosa Ziehau {
18139c6cae24SSepherosa Ziehau 	int error;
18149c6cae24SSepherosa Ziehau 
18159c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
18169c6cae24SSepherosa Ziehau 
18179c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
1818*4db5958aSJustin Hibbits 	    ("%s: transparent VF was enabled", if_name(sc->hn_ifp)));
18199c6cae24SSepherosa Ziehau 
18209c6cae24SSepherosa Ziehau 	if (bootverbose) {
18219c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "try bringing up %s\n",
1822*4db5958aSJustin Hibbits 		    if_name(sc->hn_vf_ifp));
18239c6cae24SSepherosa Ziehau 	}
18249c6cae24SSepherosa Ziehau 
18259c6cae24SSepherosa Ziehau 	/*
18269c6cae24SSepherosa Ziehau 	 * Bring the VF up.
18279c6cae24SSepherosa Ziehau 	 */
18289c6cae24SSepherosa Ziehau 	hn_xpnt_vf_saveifflags(sc);
1829*4db5958aSJustin Hibbits 	if_setflagbits(sc->hn_ifp, IFF_UP, 0);
18309c6cae24SSepherosa Ziehau 	error = hn_xpnt_vf_iocsetflags(sc);
18319c6cae24SSepherosa Ziehau 	if (error) {
18329c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "bringing up %s failed: %d\n",
1833*4db5958aSJustin Hibbits 		    if_name(sc->hn_vf_ifp), error);
18349c6cae24SSepherosa Ziehau 		return;
18359c6cae24SSepherosa Ziehau 	}
18369c6cae24SSepherosa Ziehau 
18379c6cae24SSepherosa Ziehau 	/*
18389c6cae24SSepherosa Ziehau 	 * NOTE:
18399c6cae24SSepherosa Ziehau 	 * Datapath setting must happen _after_ bringing the VF up.
18409c6cae24SSepherosa Ziehau 	 */
18419c6cae24SSepherosa Ziehau 	hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
18429c6cae24SSepherosa Ziehau 
1843642ec226SSepherosa Ziehau 	/*
1844642ec226SSepherosa Ziehau 	 * NOTE:
1845642ec226SSepherosa Ziehau 	 * Fixup RSS related bits _after_ the VF is brought up, since
1846642ec226SSepherosa Ziehau 	 * many VFs generate RSS key during it's initialization.
1847642ec226SSepherosa Ziehau 	 */
1848642ec226SSepherosa Ziehau 	hn_vf_rss_fixup(sc, true);
1849642ec226SSepherosa Ziehau 
1850a97fff19SSepherosa Ziehau 	/* Mark transparent mode VF as enabled. */
1851a97fff19SSepherosa Ziehau 	hn_xpnt_vf_setenable(sc);
18529c6cae24SSepherosa Ziehau }
18539c6cae24SSepherosa Ziehau 
18549c6cae24SSepherosa Ziehau static void
18559c6cae24SSepherosa Ziehau hn_xpnt_vf_init_taskfunc(void *xsc, int pending __unused)
18569c6cae24SSepherosa Ziehau {
18579c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
18589c6cae24SSepherosa Ziehau 
18599c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
18609c6cae24SSepherosa Ziehau 
18619c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
18629c6cae24SSepherosa Ziehau 		goto done;
18639c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
18649c6cae24SSepherosa Ziehau 		goto done;
18659c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
18669c6cae24SSepherosa Ziehau 		goto done;
18679c6cae24SSepherosa Ziehau 
18689c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick != 0) {
18699c6cae24SSepherosa Ziehau 		/* Mark VF as ready. */
18709c6cae24SSepherosa Ziehau 		hn_xpnt_vf_setready(sc);
18719c6cae24SSepherosa Ziehau 	}
18729c6cae24SSepherosa Ziehau 
1873*4db5958aSJustin Hibbits 	if (if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) {
18749c6cae24SSepherosa Ziehau 		/*
18759c6cae24SSepherosa Ziehau 		 * Delayed VF initialization.
18769c6cae24SSepherosa Ziehau 		 */
18779c6cae24SSepherosa Ziehau 		if (bootverbose) {
18789c6cae24SSepherosa Ziehau 			if_printf(sc->hn_ifp, "delayed initialize %s\n",
1879*4db5958aSJustin Hibbits 			    if_name(sc->hn_vf_ifp));
18809c6cae24SSepherosa Ziehau 		}
18819c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
18829c6cae24SSepherosa Ziehau 	}
18839c6cae24SSepherosa Ziehau done:
18849c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
18859c6cae24SSepherosa Ziehau }
18869c6cae24SSepherosa Ziehau 
1887499c3e17SSepherosa Ziehau static void
1888*4db5958aSJustin Hibbits hn_ifnet_attevent(void *xsc, if_t ifp)
1889499c3e17SSepherosa Ziehau {
1890499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1891499c3e17SSepherosa Ziehau 
1892499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1893499c3e17SSepherosa Ziehau 
1894499c3e17SSepherosa Ziehau 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
1895499c3e17SSepherosa Ziehau 		goto done;
1896499c3e17SSepherosa Ziehau 
1897499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1898499c3e17SSepherosa Ziehau 		goto done;
1899499c3e17SSepherosa Ziehau 
1900499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp != NULL) {
1901499c3e17SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s was attached as VF\n",
1902*4db5958aSJustin Hibbits 		    if_name(sc->hn_vf_ifp));
1903499c3e17SSepherosa Ziehau 		goto done;
1904499c3e17SSepherosa Ziehau 	}
1905499c3e17SSepherosa Ziehau 
1906*4db5958aSJustin Hibbits 	if (hn_xpnt_vf && if_getstartfn(ifp) != NULL) {
19079c6cae24SSepherosa Ziehau 		/*
19089c6cae24SSepherosa Ziehau 		 * ifnet.if_start is _not_ supported by transparent
19099c6cae24SSepherosa Ziehau 		 * mode VF; mainly due to the IFF_DRV_OACTIVE flag.
19109c6cae24SSepherosa Ziehau 		 */
19119c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s uses if_start, which is unsupported "
1912*4db5958aSJustin Hibbits 		    "in transparent VF mode.\n", if_name(sc->hn_vf_ifp));
1913*4db5958aSJustin Hibbits 
19149c6cae24SSepherosa Ziehau 		goto done;
19159c6cae24SSepherosa Ziehau 	}
19169c6cae24SSepherosa Ziehau 
1917499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
1918499c3e17SSepherosa Ziehau 
1919*4db5958aSJustin Hibbits 	if (if_getindex(ifp) >= hn_vfmap_size) {
1920*4db5958aSJustin Hibbits 		if_t *newmap;
1921499c3e17SSepherosa Ziehau 		int newsize;
1922499c3e17SSepherosa Ziehau 
1923*4db5958aSJustin Hibbits 		newsize = if_getindex(ifp) + HN_VFMAP_SIZE_DEF;
1924*4db5958aSJustin Hibbits 		newmap = malloc(sizeof(if_t) * newsize, M_DEVBUF,
1925499c3e17SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
1926499c3e17SSepherosa Ziehau 
1927499c3e17SSepherosa Ziehau 		memcpy(newmap, hn_vfmap,
1928*4db5958aSJustin Hibbits 		    sizeof(if_t) * hn_vfmap_size);
1929499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
1930499c3e17SSepherosa Ziehau 		hn_vfmap = newmap;
1931499c3e17SSepherosa Ziehau 		hn_vfmap_size = newsize;
1932499c3e17SSepherosa Ziehau 	}
1933*4db5958aSJustin Hibbits 	KASSERT(hn_vfmap[if_getindex(ifp)] == NULL,
1934499c3e17SSepherosa Ziehau 	    ("%s: ifindex %d was mapped to %s",
1935*4db5958aSJustin Hibbits 	     if_name(ifp), if_getindex(ifp), if_name(hn_vfmap[if_getindex(ifp)])));
1936*4db5958aSJustin Hibbits 	hn_vfmap[if_getindex(ifp)] = sc->hn_ifp;
1937499c3e17SSepherosa Ziehau 
1938499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
1939499c3e17SSepherosa Ziehau 
19409c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
19419c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
19429c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
1943*4db5958aSJustin Hibbits 	    ("%s: transparent VF was enabled", if_name(sc->hn_ifp)));
1944499c3e17SSepherosa Ziehau 	sc->hn_vf_ifp = ifp;
19459c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
19469c6cae24SSepherosa Ziehau 
19479c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
19489c6cae24SSepherosa Ziehau 		int wait_ticks;
19499c6cae24SSepherosa Ziehau 
19509c6cae24SSepherosa Ziehau 		/*
19519c6cae24SSepherosa Ziehau 		 * Install if_input for vf_ifp, which does vf_ifp -> hn_ifp.
19529c6cae24SSepherosa Ziehau 		 * Save vf_ifp's current if_input for later restoration.
19539c6cae24SSepherosa Ziehau 		 */
1954*4db5958aSJustin Hibbits 		sc->hn_vf_input = if_getinputfn(ifp);
1955*4db5958aSJustin Hibbits 		if_setinputfn(ifp, hn_xpnt_vf_input);
19569c6cae24SSepherosa Ziehau 
19579c6cae24SSepherosa Ziehau 		/*
19589c6cae24SSepherosa Ziehau 		 * Stop link status management; use the VF's.
19599c6cae24SSepherosa Ziehau 		 */
19609c6cae24SSepherosa Ziehau 		hn_suspend_mgmt(sc);
19619c6cae24SSepherosa Ziehau 
19629c6cae24SSepherosa Ziehau 		/*
19639c6cae24SSepherosa Ziehau 		 * Give VF sometime to complete its attach routing.
19649c6cae24SSepherosa Ziehau 		 */
19659c6cae24SSepherosa Ziehau 		wait_ticks = hn_xpnt_vf_attwait * hz;
19669c6cae24SSepherosa Ziehau 		sc->hn_vf_rdytick = ticks + wait_ticks;
19679c6cae24SSepherosa Ziehau 
19689c6cae24SSepherosa Ziehau 		taskqueue_enqueue_timeout(sc->hn_vf_taskq, &sc->hn_vf_init,
19699c6cae24SSepherosa Ziehau 		    wait_ticks);
19709c6cae24SSepherosa Ziehau 	}
1971499c3e17SSepherosa Ziehau done:
1972499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
1973499c3e17SSepherosa Ziehau }
1974499c3e17SSepherosa Ziehau 
1975499c3e17SSepherosa Ziehau static void
1976*4db5958aSJustin Hibbits hn_ifnet_detevent(void *xsc, if_t ifp)
1977499c3e17SSepherosa Ziehau {
1978499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1979499c3e17SSepherosa Ziehau 
1980499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1981499c3e17SSepherosa Ziehau 
1982499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
1983499c3e17SSepherosa Ziehau 		goto done;
1984499c3e17SSepherosa Ziehau 
1985499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1986499c3e17SSepherosa Ziehau 		goto done;
1987499c3e17SSepherosa Ziehau 
19889c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
19899c6cae24SSepherosa Ziehau 		/*
19909c6cae24SSepherosa Ziehau 		 * Make sure that the delayed initialization is not running.
19919c6cae24SSepherosa Ziehau 		 *
19929c6cae24SSepherosa Ziehau 		 * NOTE:
19939c6cae24SSepherosa Ziehau 		 * - This lock _must_ be released, since the hn_vf_init task
19949c6cae24SSepherosa Ziehau 		 *   will try holding this lock.
19959c6cae24SSepherosa Ziehau 		 * - It is safe to release this lock here, since the
19969c6cae24SSepherosa Ziehau 		 *   hn_ifnet_attevent() is interlocked by the hn_vf_ifp.
19979c6cae24SSepherosa Ziehau 		 *
19989c6cae24SSepherosa Ziehau 		 * XXX racy, if hn(4) ever detached.
19999c6cae24SSepherosa Ziehau 		 */
20009c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
20019c6cae24SSepherosa Ziehau 		taskqueue_drain_timeout(sc->hn_vf_taskq, &sc->hn_vf_init);
20029c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
20039c6cae24SSepherosa Ziehau 
20049c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_input != NULL, ("%s VF input is not saved",
2005*4db5958aSJustin Hibbits 		    if_name(sc->hn_ifp)));
2006*4db5958aSJustin Hibbits 		if_setinputfn(ifp, sc->hn_vf_input);
20079c6cae24SSepherosa Ziehau 		sc->hn_vf_input = NULL;
20089c6cae24SSepherosa Ziehau 
2009642ec226SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) &&
2010642ec226SSepherosa Ziehau 		    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED))
20119c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
20129c6cae24SSepherosa Ziehau 
20139c6cae24SSepherosa Ziehau 		if (sc->hn_vf_rdytick == 0) {
20149c6cae24SSepherosa Ziehau 			/*
20159c6cae24SSepherosa Ziehau 			 * The VF was ready; restore some settings.
20169c6cae24SSepherosa Ziehau 			 */
2017*4db5958aSJustin Hibbits 			if_setcapabilities(ifp, sc->hn_saved_caps);
20189c6cae24SSepherosa Ziehau 			/*
20199c6cae24SSepherosa Ziehau 			 * NOTE:
20209c6cae24SSepherosa Ziehau 			 * There is _no_ need to fixup if_capenable and
20219c6cae24SSepherosa Ziehau 			 * if_hwassist, since the if_capabilities before
20229c6cae24SSepherosa Ziehau 			 * restoration was an intersection of the VF's
20239c6cae24SSepherosa Ziehau 			 * if_capabilites and the synthetic device's
20249c6cae24SSepherosa Ziehau 			 * if_capabilites.
20259c6cae24SSepherosa Ziehau 			 */
2026*4db5958aSJustin Hibbits 			if_sethwtsomax(ifp, sc->hn_saved_tsomax);
2027*4db5958aSJustin Hibbits 			if_sethwtsomaxsegcount(sc->hn_ifp,
2028*4db5958aSJustin Hibbits 			    sc->hn_saved_tsosegcnt);
2029*4db5958aSJustin Hibbits 			if_sethwtsomaxsegsize(ifp, sc->hn_saved_tsosegsz);
20309c6cae24SSepherosa Ziehau 		}
20319c6cae24SSepherosa Ziehau 
2032642ec226SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
2033642ec226SSepherosa Ziehau 			/*
2034642ec226SSepherosa Ziehau 			 * Restore RSS settings.
2035642ec226SSepherosa Ziehau 			 */
2036642ec226SSepherosa Ziehau 			hn_vf_rss_restore(sc);
2037642ec226SSepherosa Ziehau 
20389c6cae24SSepherosa Ziehau 			/*
20399c6cae24SSepherosa Ziehau 			 * Resume link status management, which was suspended
20409c6cae24SSepherosa Ziehau 			 * by hn_ifnet_attevent().
20419c6cae24SSepherosa Ziehau 			 */
20429c6cae24SSepherosa Ziehau 			hn_resume_mgmt(sc);
20439c6cae24SSepherosa Ziehau 		}
2044642ec226SSepherosa Ziehau 	}
20459c6cae24SSepherosa Ziehau 
2046a97fff19SSepherosa Ziehau 	/* Mark transparent mode VF as disabled. */
2047a97fff19SSepherosa Ziehau 	hn_xpnt_vf_setdisable(sc, true /* clear hn_vf_ifp */);
2048499c3e17SSepherosa Ziehau 
2049499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
2050499c3e17SSepherosa Ziehau 
2051*4db5958aSJustin Hibbits 	KASSERT(if_getindex(ifp) < hn_vfmap_size,
2052*4db5958aSJustin Hibbits 	    ("ifindex %d, vfmapsize %d", if_getindex(ifp), hn_vfmap_size));
2053*4db5958aSJustin Hibbits 	if (hn_vfmap[if_getindex(ifp)] != NULL) {
2054*4db5958aSJustin Hibbits 		KASSERT(hn_vfmap[if_getindex(ifp)] == sc->hn_ifp,
2055499c3e17SSepherosa Ziehau 		    ("%s: ifindex %d was mapped to %s",
2056*4db5958aSJustin Hibbits 		     if_name(ifp), if_getindex(ifp),
2057*4db5958aSJustin Hibbits 		     if_name(hn_vfmap[if_getindex(ifp)])));
2058*4db5958aSJustin Hibbits 		hn_vfmap[if_getindex(ifp)] = NULL;
2059499c3e17SSepherosa Ziehau 	}
2060499c3e17SSepherosa Ziehau 
2061499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
2062499c3e17SSepherosa Ziehau done:
2063499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
2064499c3e17SSepherosa Ziehau }
2065499c3e17SSepherosa Ziehau 
20669c6cae24SSepherosa Ziehau static void
2067*4db5958aSJustin Hibbits hn_ifnet_lnkevent(void *xsc, if_t ifp, int link_state)
20689c6cae24SSepherosa Ziehau {
20699c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
20709c6cae24SSepherosa Ziehau 
20719c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == ifp)
20729c6cae24SSepherosa Ziehau 		if_link_state_change(sc->hn_ifp, link_state);
20739c6cae24SSepherosa Ziehau }
20749c6cae24SSepherosa Ziehau 
207515516c77SSepherosa Ziehau static int
2076*4db5958aSJustin Hibbits hn_tsomax_sysctl(SYSCTL_HANDLER_ARGS)
2077*4db5958aSJustin Hibbits {
2078*4db5958aSJustin Hibbits 	struct hn_softc *sc = arg1;
2079*4db5958aSJustin Hibbits 	unsigned int tsomax;
2080*4db5958aSJustin Hibbits 	int error;
2081*4db5958aSJustin Hibbits 
2082*4db5958aSJustin Hibbits 	tsomax = if_gethwtsomax(sc->hn_ifp);
2083*4db5958aSJustin Hibbits 	error = sysctl_handle_int(oidp, &tsomax, 0, req);
2084*4db5958aSJustin Hibbits 	return error;
2085*4db5958aSJustin Hibbits }
2086*4db5958aSJustin Hibbits 
2087*4db5958aSJustin Hibbits static int
2088*4db5958aSJustin Hibbits hn_tsomaxsegcnt_sysctl(SYSCTL_HANDLER_ARGS)
2089*4db5958aSJustin Hibbits {
2090*4db5958aSJustin Hibbits 	struct hn_softc *sc = arg1;
2091*4db5958aSJustin Hibbits 	unsigned int tsomaxsegcnt;
2092*4db5958aSJustin Hibbits 	int error;
2093*4db5958aSJustin Hibbits 
2094*4db5958aSJustin Hibbits 	tsomaxsegcnt = if_gethwtsomaxsegcount(sc->hn_ifp);
2095*4db5958aSJustin Hibbits 	error = sysctl_handle_int(oidp, &tsomaxsegcnt, 0, req);
2096*4db5958aSJustin Hibbits 	return error;
2097*4db5958aSJustin Hibbits }
2098*4db5958aSJustin Hibbits 
2099*4db5958aSJustin Hibbits static int
2100*4db5958aSJustin Hibbits hn_tsomaxsegsz_sysctl(SYSCTL_HANDLER_ARGS)
2101*4db5958aSJustin Hibbits {
2102*4db5958aSJustin Hibbits 	struct hn_softc *sc = arg1;
2103*4db5958aSJustin Hibbits 	unsigned int tsomaxsegsz;
2104*4db5958aSJustin Hibbits 	int error;
2105*4db5958aSJustin Hibbits 
2106*4db5958aSJustin Hibbits 	tsomaxsegsz = if_gethwtsomaxsegsize(sc->hn_ifp);
2107*4db5958aSJustin Hibbits 	error = sysctl_handle_int(oidp, &tsomaxsegsz, 0, req);
2108*4db5958aSJustin Hibbits 	return error;
2109*4db5958aSJustin Hibbits }
2110*4db5958aSJustin Hibbits 
2111*4db5958aSJustin Hibbits static int
211215516c77SSepherosa Ziehau hn_probe(device_t dev)
211315516c77SSepherosa Ziehau {
211415516c77SSepherosa Ziehau 
2115c2d50b26SSepherosa Ziehau 	if (VMBUS_PROBE_GUID(device_get_parent(dev), dev, &hn_guid) == 0) {
211615516c77SSepherosa Ziehau 		device_set_desc(dev, "Hyper-V Network Interface");
211715516c77SSepherosa Ziehau 		return BUS_PROBE_DEFAULT;
211815516c77SSepherosa Ziehau 	}
211915516c77SSepherosa Ziehau 	return ENXIO;
212015516c77SSepherosa Ziehau }
212115516c77SSepherosa Ziehau 
212215516c77SSepherosa Ziehau static int
212315516c77SSepherosa Ziehau hn_attach(device_t dev)
212415516c77SSepherosa Ziehau {
212515516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
212615516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
212715516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
212815516c77SSepherosa Ziehau 	uint8_t eaddr[ETHER_ADDR_LEN];
2129*4db5958aSJustin Hibbits 	if_t ifp = NULL;
213015516c77SSepherosa Ziehau 	int error, ring_cnt, tx_ring_cnt;
2131eb2fe044SSepherosa Ziehau 	uint32_t mtu;
213215516c77SSepherosa Ziehau 
213315516c77SSepherosa Ziehau 	sc->hn_dev = dev;
213415516c77SSepherosa Ziehau 	sc->hn_prichan = vmbus_get_channel(dev);
213515516c77SSepherosa Ziehau 	HN_LOCK_INIT(sc);
21369c6cae24SSepherosa Ziehau 	rm_init(&sc->hn_vf_lock, "hnvf");
21379c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_xpnt_vf_accbpf)
21389c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
213915516c77SSepherosa Ziehau 
214015516c77SSepherosa Ziehau 	/*
2141dc13fee6SSepherosa Ziehau 	 * Initialize these tunables once.
2142dc13fee6SSepherosa Ziehau 	 */
2143dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = hn_tx_agg_size;
2144dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = hn_tx_agg_pkts;
2145dc13fee6SSepherosa Ziehau 
2146dc13fee6SSepherosa Ziehau 	/*
214715516c77SSepherosa Ziehau 	 * Setup taskqueue for transmission.
214815516c77SSepherosa Ziehau 	 */
21490e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_INDEP) {
2150fdd0222aSSepherosa Ziehau 		int i;
2151fdd0222aSSepherosa Ziehau 
2152fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs =
2153fdd0222aSSepherosa Ziehau 		    malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
2154fdd0222aSSepherosa Ziehau 		    M_DEVBUF, M_WAITOK);
2155fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i) {
2156fdd0222aSSepherosa Ziehau 			sc->hn_tx_taskqs[i] = taskqueue_create("hn_tx",
2157fdd0222aSSepherosa Ziehau 			    M_WAITOK, taskqueue_thread_enqueue,
2158fdd0222aSSepherosa Ziehau 			    &sc->hn_tx_taskqs[i]);
2159fdd0222aSSepherosa Ziehau 			taskqueue_start_threads(&sc->hn_tx_taskqs[i], 1, PI_NET,
2160fdd0222aSSepherosa Ziehau 			    "%s tx%d", device_get_nameunit(dev), i);
2161fdd0222aSSepherosa Ziehau 		}
21620e11868dSSepherosa Ziehau 	} else if (hn_tx_taskq_mode == HN_TX_TASKQ_M_GLOBAL) {
2163fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs = hn_tx_taskque;
216415516c77SSepherosa Ziehau 	}
216515516c77SSepherosa Ziehau 
216615516c77SSepherosa Ziehau 	/*
216715516c77SSepherosa Ziehau 	 * Setup taskqueue for mangement tasks, e.g. link status.
216815516c77SSepherosa Ziehau 	 */
216915516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq0 = taskqueue_create("hn_mgmt", M_WAITOK,
217015516c77SSepherosa Ziehau 	    taskqueue_thread_enqueue, &sc->hn_mgmt_taskq0);
217115516c77SSepherosa Ziehau 	taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
217215516c77SSepherosa Ziehau 	    device_get_nameunit(dev));
217315516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc);
217415516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_netchg_init, 0, hn_netchg_init_taskfunc, sc);
217515516c77SSepherosa Ziehau 	TIMEOUT_TASK_INIT(sc->hn_mgmt_taskq0, &sc->hn_netchg_status, 0,
217615516c77SSepherosa Ziehau 	    hn_netchg_status_taskfunc, sc);
217715516c77SSepherosa Ziehau 
21789c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
21799c6cae24SSepherosa Ziehau 		/*
21809c6cae24SSepherosa Ziehau 		 * Setup taskqueue for VF tasks, e.g. delayed VF bringing up.
21819c6cae24SSepherosa Ziehau 		 */
21829c6cae24SSepherosa Ziehau 		sc->hn_vf_taskq = taskqueue_create("hn_vf", M_WAITOK,
21839c6cae24SSepherosa Ziehau 		    taskqueue_thread_enqueue, &sc->hn_vf_taskq);
21849c6cae24SSepherosa Ziehau 		taskqueue_start_threads(&sc->hn_vf_taskq, 1, PI_NET, "%s vf",
21859c6cae24SSepherosa Ziehau 		    device_get_nameunit(dev));
21869c6cae24SSepherosa Ziehau 		TIMEOUT_TASK_INIT(sc->hn_vf_taskq, &sc->hn_vf_init, 0,
21879c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_init_taskfunc, sc);
21889c6cae24SSepherosa Ziehau 	}
21899c6cae24SSepherosa Ziehau 
219015516c77SSepherosa Ziehau 	/*
219115516c77SSepherosa Ziehau 	 * Allocate ifnet and setup its name earlier, so that if_printf
219215516c77SSepherosa Ziehau 	 * can be used by functions, which will be called after
219315516c77SSepherosa Ziehau 	 * ether_ifattach().
219415516c77SSepherosa Ziehau 	 */
219515516c77SSepherosa Ziehau 	ifp = sc->hn_ifp = if_alloc(IFT_ETHER);
2196*4db5958aSJustin Hibbits 	if_setsoftc(ifp, sc);
219715516c77SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
219815516c77SSepherosa Ziehau 
219915516c77SSepherosa Ziehau 	/*
220015516c77SSepherosa Ziehau 	 * Initialize ifmedia earlier so that it can be unconditionally
220115516c77SSepherosa Ziehau 	 * destroyed, if error happened later on.
220215516c77SSepherosa Ziehau 	 */
220315516c77SSepherosa Ziehau 	ifmedia_init(&sc->hn_media, 0, hn_ifmedia_upd, hn_ifmedia_sts);
220415516c77SSepherosa Ziehau 
220515516c77SSepherosa Ziehau 	/*
220615516c77SSepherosa Ziehau 	 * Figure out the # of RX rings (ring_cnt) and the # of TX rings
220715516c77SSepherosa Ziehau 	 * to use (tx_ring_cnt).
220815516c77SSepherosa Ziehau 	 *
220915516c77SSepherosa Ziehau 	 * NOTE:
221015516c77SSepherosa Ziehau 	 * The # of RX rings to use is same as the # of channels to use.
221115516c77SSepherosa Ziehau 	 */
221215516c77SSepherosa Ziehau 	ring_cnt = hn_chan_cnt;
221315516c77SSepherosa Ziehau 	if (ring_cnt <= 0) {
221415516c77SSepherosa Ziehau 		/* Default */
221515516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
221615516c77SSepherosa Ziehau 		if (ring_cnt > HN_RING_CNT_DEF_MAX)
221715516c77SSepherosa Ziehau 			ring_cnt = HN_RING_CNT_DEF_MAX;
221815516c77SSepherosa Ziehau 	} else if (ring_cnt > mp_ncpus) {
221915516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
222015516c77SSepherosa Ziehau 	}
222134d68912SSepherosa Ziehau #ifdef RSS
222234d68912SSepherosa Ziehau 	if (ring_cnt > rss_getnumbuckets())
222334d68912SSepherosa Ziehau 		ring_cnt = rss_getnumbuckets();
222434d68912SSepherosa Ziehau #endif
222515516c77SSepherosa Ziehau 
222615516c77SSepherosa Ziehau 	tx_ring_cnt = hn_tx_ring_cnt;
222715516c77SSepherosa Ziehau 	if (tx_ring_cnt <= 0 || tx_ring_cnt > ring_cnt)
222815516c77SSepherosa Ziehau 		tx_ring_cnt = ring_cnt;
222923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
223015516c77SSepherosa Ziehau 	if (hn_use_if_start) {
223115516c77SSepherosa Ziehau 		/* ifnet.if_start only needs one TX ring. */
223215516c77SSepherosa Ziehau 		tx_ring_cnt = 1;
223315516c77SSepherosa Ziehau 	}
223423bf9e15SSepherosa Ziehau #endif
223515516c77SSepherosa Ziehau 
223615516c77SSepherosa Ziehau 	/*
223715516c77SSepherosa Ziehau 	 * Set the leader CPU for channels.
223815516c77SSepherosa Ziehau 	 */
223915516c77SSepherosa Ziehau 	sc->hn_cpu = atomic_fetchadd_int(&hn_cpu_index, ring_cnt) % mp_ncpus;
224015516c77SSepherosa Ziehau 
224115516c77SSepherosa Ziehau 	/*
224215516c77SSepherosa Ziehau 	 * Create enough TX/RX rings, even if only limited number of
224315516c77SSepherosa Ziehau 	 * channels can be allocated.
224415516c77SSepherosa Ziehau 	 */
224515516c77SSepherosa Ziehau 	error = hn_create_tx_data(sc, tx_ring_cnt);
224615516c77SSepherosa Ziehau 	if (error)
224715516c77SSepherosa Ziehau 		goto failed;
224815516c77SSepherosa Ziehau 	error = hn_create_rx_data(sc, ring_cnt);
224915516c77SSepherosa Ziehau 	if (error)
225015516c77SSepherosa Ziehau 		goto failed;
225115516c77SSepherosa Ziehau 
225215516c77SSepherosa Ziehau 	/*
225315516c77SSepherosa Ziehau 	 * Create transaction context for NVS and RNDIS transactions.
225415516c77SSepherosa Ziehau 	 */
225515516c77SSepherosa Ziehau 	sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
225615516c77SSepherosa Ziehau 	    HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
225725641fc7SSepherosa Ziehau 	if (sc->hn_xact == NULL) {
225825641fc7SSepherosa Ziehau 		error = ENXIO;
225915516c77SSepherosa Ziehau 		goto failed;
226025641fc7SSepherosa Ziehau 	}
226125641fc7SSepherosa Ziehau 
226225641fc7SSepherosa Ziehau 	/*
226325641fc7SSepherosa Ziehau 	 * Install orphan handler for the revocation of this device's
226425641fc7SSepherosa Ziehau 	 * primary channel.
226525641fc7SSepherosa Ziehau 	 *
226625641fc7SSepherosa Ziehau 	 * NOTE:
226725641fc7SSepherosa Ziehau 	 * The processing order is critical here:
226825641fc7SSepherosa Ziehau 	 * Install the orphan handler, _before_ testing whether this
226925641fc7SSepherosa Ziehau 	 * device's primary channel has been revoked or not.
227025641fc7SSepherosa Ziehau 	 */
227125641fc7SSepherosa Ziehau 	vmbus_chan_set_orphan(sc->hn_prichan, sc->hn_xact);
227225641fc7SSepherosa Ziehau 	if (vmbus_chan_is_revoked(sc->hn_prichan)) {
227325641fc7SSepherosa Ziehau 		error = ENXIO;
227425641fc7SSepherosa Ziehau 		goto failed;
227525641fc7SSepherosa Ziehau 	}
227615516c77SSepherosa Ziehau 
227715516c77SSepherosa Ziehau 	/*
227815516c77SSepherosa Ziehau 	 * Attach the synthetic parts, i.e. NVS and RNDIS.
227915516c77SSepherosa Ziehau 	 */
228015516c77SSepherosa Ziehau 	error = hn_synth_attach(sc, ETHERMTU);
228115516c77SSepherosa Ziehau 	if (error)
228215516c77SSepherosa Ziehau 		goto failed;
228315516c77SSepherosa Ziehau 
228415516c77SSepherosa Ziehau 	error = hn_rndis_get_eaddr(sc, eaddr);
228515516c77SSepherosa Ziehau 	if (error)
228615516c77SSepherosa Ziehau 		goto failed;
228715516c77SSepherosa Ziehau 
2288eb2fe044SSepherosa Ziehau 	error = hn_rndis_get_mtu(sc, &mtu);
2289eb2fe044SSepherosa Ziehau 	if (error)
2290eb2fe044SSepherosa Ziehau 		mtu = ETHERMTU;
2291eb2fe044SSepherosa Ziehau 	else if (bootverbose)
2292eb2fe044SSepherosa Ziehau 		device_printf(dev, "RNDIS mtu %u\n", mtu);
2293eb2fe044SSepherosa Ziehau 
229415516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
229515516c77SSepherosa Ziehau 		/*
229615516c77SSepherosa Ziehau 		 * Reduce TCP segment aggregation limit for multiple
229715516c77SSepherosa Ziehau 		 * RX rings to increase ACK timeliness.
229815516c77SSepherosa Ziehau 		 */
229915516c77SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF);
230015516c77SSepherosa Ziehau 	}
230115516c77SSepherosa Ziehau 
230215516c77SSepherosa Ziehau 	/*
2303db76829bSSepherosa Ziehau 	 * Fixup TX/RX stuffs after synthetic parts are attached.
230415516c77SSepherosa Ziehau 	 */
230515516c77SSepherosa Ziehau 	hn_fixup_tx_data(sc);
2306db76829bSSepherosa Ziehau 	hn_fixup_rx_data(sc);
230715516c77SSepherosa Ziehau 
230815516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
230915516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
231015516c77SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "nvs_version", CTLFLAG_RD,
231115516c77SSepherosa Ziehau 	    &sc->hn_nvs_ver, 0, "NVS version");
231215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ndis_version",
231315516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
231415516c77SSepherosa Ziehau 	    hn_ndis_version_sysctl, "A", "NDIS version");
231515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "caps",
231615516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
231715516c77SSepherosa Ziehau 	    hn_caps_sysctl, "A", "capabilities");
231815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist",
231915516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
232015516c77SSepherosa Ziehau 	    hn_hwassist_sysctl, "A", "hwassist");
2321*4db5958aSJustin Hibbits 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tso_max",
2322*4db5958aSJustin Hibbits 	    CTLTYPE_UINT | CTLFLAG_RD, sc, 0, hn_tsomax_sysctl,
2323*4db5958aSJustin Hibbits 	    "IU", "max TSO size");
2324*4db5958aSJustin Hibbits 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tso_maxsegcnt",
2325*4db5958aSJustin Hibbits 	    CTLTYPE_UINT | CTLFLAG_RD, sc, 0, hn_tsomaxsegcnt_sysctl,
2326*4db5958aSJustin Hibbits 	    "IU", "max # of TSO segments");
2327*4db5958aSJustin Hibbits 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tso_maxsegsz",
2328*4db5958aSJustin Hibbits 	    CTLTYPE_UINT | CTLFLAG_RD, sc, 0, hn_tsomaxsegsz_sysctl,
2329*4db5958aSJustin Hibbits 	    "IU", "max size of TSO segment");
233015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter",
233115516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
233215516c77SSepherosa Ziehau 	    hn_rxfilter_sysctl, "A", "rxfilter");
233315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hash",
233415516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
233515516c77SSepherosa Ziehau 	    hn_rss_hash_sysctl, "A", "RSS hash");
2336642ec226SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hashcap",
2337642ec226SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2338642ec226SSepherosa Ziehau 	    hn_rss_hcap_sysctl, "A", "RSS hash capabilities");
2339642ec226SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "mbuf_hash",
2340642ec226SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2341642ec226SSepherosa Ziehau 	    hn_rss_mbuf_sysctl, "A", "RSS hash for mbufs");
234215516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rss_ind_size",
234315516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rss_ind_size, 0, "RSS indirect entry count");
234434d68912SSepherosa Ziehau #ifndef RSS
234534d68912SSepherosa Ziehau 	/*
234634d68912SSepherosa Ziehau 	 * Don't allow RSS key/indirect table changes, if RSS is defined.
234734d68912SSepherosa Ziehau 	 */
234815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key",
234915516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
235015516c77SSepherosa Ziehau 	    hn_rss_key_sysctl, "IU", "RSS key");
235115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_ind",
235215516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
235315516c77SSepherosa Ziehau 	    hn_rss_ind_sysctl, "IU", "RSS indirect table");
235434d68912SSepherosa Ziehau #endif
2355dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_size",
2356dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_size, 0,
2357dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation size limit");
2358dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_pkts",
2359dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_pkts, 0,
2360dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation count limit");
2361dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_align",
2362dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_align, 0,
2363dc13fee6SSepherosa Ziehau 	    "RNDIS packet transmission aggregation alignment");
2364dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_size",
2365dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
2366dc13fee6SSepherosa Ziehau 	    hn_txagg_size_sysctl, "I",
2367dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation size, 0 -- disable, -1 -- auto");
2368dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pkts",
2369dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
2370dc13fee6SSepherosa Ziehau 	    hn_txagg_pkts_sysctl, "I",
2371dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation packets, "
2372dc13fee6SSepherosa Ziehau 	    "0 -- disable, -1 -- auto");
23736c1204dfSSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "polling",
23746c1204dfSSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
23756c1204dfSSepherosa Ziehau 	    hn_polling_sysctl, "I",
23766c1204dfSSepherosa Ziehau 	    "Polling frequency: [100,1000000], 0 disable polling");
237740d60d6eSDexuan Cui 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf",
237840d60d6eSDexuan Cui 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
237940d60d6eSDexuan Cui 	    hn_vf_sysctl, "A", "Virtual Function's name");
23809c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
2381499c3e17SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxvf",
2382499c3e17SSepherosa Ziehau 		    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2383499c3e17SSepherosa Ziehau 		    hn_rxvf_sysctl, "A", "activated Virtual Function's name");
23849c6cae24SSepherosa Ziehau 	} else {
23859c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_enabled",
23869c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
23879c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_enabled_sysctl, "I",
23889c6cae24SSepherosa Ziehau 		    "Transparent VF enabled");
23899c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_accbpf",
23909c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
23919c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_accbpf_sysctl, "I",
23929c6cae24SSepherosa Ziehau 		    "Accurate BPF for transparent VF");
23939c6cae24SSepherosa Ziehau 	}
239415516c77SSepherosa Ziehau 
239580c3eb7bSWei Hu 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rsc_switch",
239680c3eb7bSWei Hu 	    CTLTYPE_UINT | CTLFLAG_RW, sc, 0, hn_rsc_sysctl, "A",
239780c3eb7bSWei Hu 	    "switch to rsc");
239880c3eb7bSWei Hu 
239915516c77SSepherosa Ziehau 	/*
240015516c77SSepherosa Ziehau 	 * Setup the ifmedia, which has been initialized earlier.
240115516c77SSepherosa Ziehau 	 */
240215516c77SSepherosa Ziehau 	ifmedia_add(&sc->hn_media, IFM_ETHER | IFM_AUTO, 0, NULL);
240315516c77SSepherosa Ziehau 	ifmedia_set(&sc->hn_media, IFM_ETHER | IFM_AUTO);
240415516c77SSepherosa Ziehau 	/* XXX ifmedia_set really should do this for us */
240515516c77SSepherosa Ziehau 	sc->hn_media.ifm_media = sc->hn_media.ifm_cur->ifm_media;
240615516c77SSepherosa Ziehau 
240715516c77SSepherosa Ziehau 	/*
240815516c77SSepherosa Ziehau 	 * Setup the ifnet for this interface.
240915516c77SSepherosa Ziehau 	 */
241015516c77SSepherosa Ziehau 
2411*4db5958aSJustin Hibbits 	if_setbaudrate(ifp, IF_Gbps(10));
2412*4db5958aSJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
2413*4db5958aSJustin Hibbits 	if_setioctlfn(ifp, hn_ioctl);
2414*4db5958aSJustin Hibbits 	if_setinitfn(ifp, hn_init);
241523bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
241615516c77SSepherosa Ziehau 	if (hn_use_if_start) {
241715516c77SSepherosa Ziehau 		int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]);
241815516c77SSepherosa Ziehau 
2419*4db5958aSJustin Hibbits 		if_setstartfn(ifp, hn_start);
2420*4db5958aSJustin Hibbits 		if_setsendqlen(ifp, qdepth);
2421*4db5958aSJustin Hibbits 		if_setsendqready(ifp);
242223bf9e15SSepherosa Ziehau 	} else
242323bf9e15SSepherosa Ziehau #endif
242423bf9e15SSepherosa Ziehau 	{
2425*4db5958aSJustin Hibbits 		if_settransmitfn(ifp, hn_transmit);
2426*4db5958aSJustin Hibbits 		if_setqflushfn(ifp, hn_xmit_qflush);
242715516c77SSepherosa Ziehau 	}
242815516c77SSepherosa Ziehau 
2429*4db5958aSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_RXCSUM | IFCAP_LRO | IFCAP_LINKSTATE, 0);
243015516c77SSepherosa Ziehau #ifdef foo
243115516c77SSepherosa Ziehau 	/* We can't diff IPv6 packets from IPv4 packets on RX path. */
2432*4db5958aSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_RXCSUM_IPV6, 0);
243315516c77SSepherosa Ziehau #endif
243415516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_VLAN) {
243515516c77SSepherosa Ziehau 		/* XXX not sure about VLAN_MTU. */
2436*4db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU, 0);
243715516c77SSepherosa Ziehau 	}
243815516c77SSepherosa Ziehau 
2439*4db5958aSJustin Hibbits 	if_sethwassist(ifp, sc->hn_tx_ring[0].hn_csum_assist);
2440*4db5958aSJustin Hibbits 	if (if_gethwassist(ifp) & HN_CSUM_IP_MASK)
2441*4db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_TXCSUM, 0);
2442*4db5958aSJustin Hibbits 	if (if_gethwassist(ifp) & HN_CSUM_IP6_MASK)
2443*4db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_TXCSUM_IPV6, 0);
244415516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO4) {
2445*4db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_TSO4, 0);
2446*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, CSUM_IP_TSO, 0);
244715516c77SSepherosa Ziehau 	}
244815516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO6) {
2449*4db5958aSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_TSO6, 0);
2450*4db5958aSJustin Hibbits 		if_sethwassistbits(ifp, CSUM_IP6_TSO, 0);
245115516c77SSepherosa Ziehau 	}
245215516c77SSepherosa Ziehau 
245315516c77SSepherosa Ziehau 	/* Enable all available capabilities by default. */
2454*4db5958aSJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
245515516c77SSepherosa Ziehau 
24567960e6baSSepherosa Ziehau 	/*
24577960e6baSSepherosa Ziehau 	 * Disable IPv6 TSO and TXCSUM by default, they still can
24587960e6baSSepherosa Ziehau 	 * be enabled through SIOCSIFCAP.
24597960e6baSSepherosa Ziehau 	 */
2460*4db5958aSJustin Hibbits 	if_setcapenablebit(ifp, 0, (IFCAP_TXCSUM_IPV6 | IFCAP_TSO6));
2461*4db5958aSJustin Hibbits 	if_sethwassistbits(ifp, 0, (HN_CSUM_IP6_MASK | CSUM_IP6_TSO));
24627960e6baSSepherosa Ziehau 
2463*4db5958aSJustin Hibbits 	if (if_getcapabilities(ifp) & (IFCAP_TSO6 | IFCAP_TSO4)) {
24649c6cae24SSepherosa Ziehau 		/*
24659c6cae24SSepherosa Ziehau 		 * Lock hn_set_tso_maxsize() to simplify its
24669c6cae24SSepherosa Ziehau 		 * internal logic.
24679c6cae24SSepherosa Ziehau 		 */
24689c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
246915516c77SSepherosa Ziehau 		hn_set_tso_maxsize(sc, hn_tso_maxlen, ETHERMTU);
24709c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
2471*4db5958aSJustin Hibbits 		if_sethwtsomaxsegcount(ifp, HN_TX_DATA_SEGCNT_MAX);
2472*4db5958aSJustin Hibbits 		if_sethwtsomaxsegsize(ifp, PAGE_SIZE);
247315516c77SSepherosa Ziehau 	}
247415516c77SSepherosa Ziehau 
247515516c77SSepherosa Ziehau 	ether_ifattach(ifp, eaddr);
247615516c77SSepherosa Ziehau 
2477*4db5958aSJustin Hibbits 	if ((if_getcapabilities(ifp) & (IFCAP_TSO6 | IFCAP_TSO4)) && bootverbose) {
247815516c77SSepherosa Ziehau 		if_printf(ifp, "TSO segcnt %u segsz %u\n",
2479*4db5958aSJustin Hibbits 		    if_gethwtsomaxsegcount(ifp), if_gethwtsomaxsegsize(ifp));
248015516c77SSepherosa Ziehau 	}
2481eb2fe044SSepherosa Ziehau 	if (mtu < ETHERMTU) {
2482*4db5958aSJustin Hibbits 
2483*4db5958aSJustin Hibbits 		if_setmtu(ifp, mtu);
2484eb2fe044SSepherosa Ziehau 	}
248515516c77SSepherosa Ziehau 
248615516c77SSepherosa Ziehau 	/* Inform the upper layer about the long frame support. */
2487*4db5958aSJustin Hibbits 	if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
248815516c77SSepherosa Ziehau 
248915516c77SSepherosa Ziehau 	/*
249015516c77SSepherosa Ziehau 	 * Kick off link status check.
249115516c77SSepherosa Ziehau 	 */
249215516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
249315516c77SSepherosa Ziehau 	hn_update_link_status(sc);
249415516c77SSepherosa Ziehau 
24959c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
24965bdfd3fdSDexuan Cui 		sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event,
24975bdfd3fdSDexuan Cui 		    hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY);
24985bdfd3fdSDexuan Cui 		sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event,
24995bdfd3fdSDexuan Cui 		    hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY);
25009c6cae24SSepherosa Ziehau 	} else {
25019c6cae24SSepherosa Ziehau 		sc->hn_ifnet_lnkhand = EVENTHANDLER_REGISTER(ifnet_link_event,
25029c6cae24SSepherosa Ziehau 		    hn_ifnet_lnkevent, sc, EVENTHANDLER_PRI_ANY);
25039c6cae24SSepherosa Ziehau 	}
25045bdfd3fdSDexuan Cui 
2505f41e0df4SSepherosa Ziehau 	/*
2506f41e0df4SSepherosa Ziehau 	 * NOTE:
2507f41e0df4SSepherosa Ziehau 	 * Subscribe ether_ifattach event, instead of ifnet_arrival event,
2508f41e0df4SSepherosa Ziehau 	 * since interface's LLADDR is needed; interface LLADDR is not
2509f41e0df4SSepherosa Ziehau 	 * available when ifnet_arrival event is triggered.
2510f41e0df4SSepherosa Ziehau 	 */
2511499c3e17SSepherosa Ziehau 	sc->hn_ifnet_atthand = EVENTHANDLER_REGISTER(ether_ifattach_event,
2512499c3e17SSepherosa Ziehau 	    hn_ifnet_attevent, sc, EVENTHANDLER_PRI_ANY);
2513499c3e17SSepherosa Ziehau 	sc->hn_ifnet_dethand = EVENTHANDLER_REGISTER(ifnet_departure_event,
2514499c3e17SSepherosa Ziehau 	    hn_ifnet_detevent, sc, EVENTHANDLER_PRI_ANY);
2515499c3e17SSepherosa Ziehau 
251615516c77SSepherosa Ziehau 	return (0);
251715516c77SSepherosa Ziehau failed:
251815516c77SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
251915516c77SSepherosa Ziehau 		hn_synth_detach(sc);
252015516c77SSepherosa Ziehau 	hn_detach(dev);
252115516c77SSepherosa Ziehau 	return (error);
252215516c77SSepherosa Ziehau }
252315516c77SSepherosa Ziehau 
252415516c77SSepherosa Ziehau static int
252515516c77SSepherosa Ziehau hn_detach(device_t dev)
252615516c77SSepherosa Ziehau {
252715516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
2528*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp, vf_ifp;
252915516c77SSepherosa Ziehau 
25309c6cae24SSepherosa Ziehau 	if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
25319c6cae24SSepherosa Ziehau 		/*
25329c6cae24SSepherosa Ziehau 		 * In case that the vmbus missed the orphan handler
25339c6cae24SSepherosa Ziehau 		 * installation.
25349c6cae24SSepherosa Ziehau 		 */
25359c6cae24SSepherosa Ziehau 		vmbus_xact_ctx_orphan(sc->hn_xact);
25369c6cae24SSepherosa Ziehau 	}
25379c6cae24SSepherosa Ziehau 
25385bdfd3fdSDexuan Cui 	if (sc->hn_ifaddr_evthand != NULL)
25395bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand);
25405bdfd3fdSDexuan Cui 	if (sc->hn_ifnet_evthand != NULL)
25415bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand);
2542499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_atthand != NULL) {
2543499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ether_ifattach_event,
2544499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_atthand);
2545499c3e17SSepherosa Ziehau 	}
2546499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_dethand != NULL) {
2547499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
2548499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_dethand);
2549499c3e17SSepherosa Ziehau 	}
25509c6cae24SSepherosa Ziehau 	if (sc->hn_ifnet_lnkhand != NULL)
25519c6cae24SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_link_event, sc->hn_ifnet_lnkhand);
2552499c3e17SSepherosa Ziehau 
2553499c3e17SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
2554499c3e17SSepherosa Ziehau 	__compiler_membar();
2555499c3e17SSepherosa Ziehau 	if (vf_ifp != NULL)
2556499c3e17SSepherosa Ziehau 		hn_ifnet_detevent(sc, vf_ifp);
25575bdfd3fdSDexuan Cui 
255815516c77SSepherosa Ziehau 	if (device_is_attached(dev)) {
255915516c77SSepherosa Ziehau 		HN_LOCK(sc);
256015516c77SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
2561*4db5958aSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
25625bdfd3fdSDexuan Cui 				hn_stop(sc, true);
256315516c77SSepherosa Ziehau 			/*
256415516c77SSepherosa Ziehau 			 * NOTE:
256515516c77SSepherosa Ziehau 			 * hn_stop() only suspends data, so managment
256615516c77SSepherosa Ziehau 			 * stuffs have to be suspended manually here.
256715516c77SSepherosa Ziehau 			 */
256815516c77SSepherosa Ziehau 			hn_suspend_mgmt(sc);
256915516c77SSepherosa Ziehau 			hn_synth_detach(sc);
257015516c77SSepherosa Ziehau 		}
257115516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
257215516c77SSepherosa Ziehau 		ether_ifdetach(ifp);
257315516c77SSepherosa Ziehau 	}
257415516c77SSepherosa Ziehau 
257515516c77SSepherosa Ziehau 	ifmedia_removeall(&sc->hn_media);
257615516c77SSepherosa Ziehau 	hn_destroy_rx_data(sc);
257715516c77SSepherosa Ziehau 	hn_destroy_tx_data(sc);
257815516c77SSepherosa Ziehau 
25790e11868dSSepherosa Ziehau 	if (sc->hn_tx_taskqs != NULL && sc->hn_tx_taskqs != hn_tx_taskque) {
2580fdd0222aSSepherosa Ziehau 		int i;
2581fdd0222aSSepherosa Ziehau 
2582fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
2583fdd0222aSSepherosa Ziehau 			taskqueue_free(sc->hn_tx_taskqs[i]);
2584fdd0222aSSepherosa Ziehau 		free(sc->hn_tx_taskqs, M_DEVBUF);
2585fdd0222aSSepherosa Ziehau 	}
258615516c77SSepherosa Ziehau 	taskqueue_free(sc->hn_mgmt_taskq0);
25879c6cae24SSepherosa Ziehau 	if (sc->hn_vf_taskq != NULL)
25889c6cae24SSepherosa Ziehau 		taskqueue_free(sc->hn_vf_taskq);
258915516c77SSepherosa Ziehau 
259025641fc7SSepherosa Ziehau 	if (sc->hn_xact != NULL) {
259125641fc7SSepherosa Ziehau 		/*
259225641fc7SSepherosa Ziehau 		 * Uninstall the orphan handler _before_ the xact is
259325641fc7SSepherosa Ziehau 		 * destructed.
259425641fc7SSepherosa Ziehau 		 */
259525641fc7SSepherosa Ziehau 		vmbus_chan_unset_orphan(sc->hn_prichan);
259615516c77SSepherosa Ziehau 		vmbus_xact_ctx_destroy(sc->hn_xact);
259725641fc7SSepherosa Ziehau 	}
259815516c77SSepherosa Ziehau 
259915516c77SSepherosa Ziehau 	if_free(ifp);
260015516c77SSepherosa Ziehau 
260115516c77SSepherosa Ziehau 	HN_LOCK_DESTROY(sc);
26029c6cae24SSepherosa Ziehau 	rm_destroy(&sc->hn_vf_lock);
260315516c77SSepherosa Ziehau 	return (0);
260415516c77SSepherosa Ziehau }
260515516c77SSepherosa Ziehau 
260615516c77SSepherosa Ziehau static int
260715516c77SSepherosa Ziehau hn_shutdown(device_t dev)
260815516c77SSepherosa Ziehau {
260915516c77SSepherosa Ziehau 
261015516c77SSepherosa Ziehau 	return (0);
261115516c77SSepherosa Ziehau }
261215516c77SSepherosa Ziehau 
261315516c77SSepherosa Ziehau static void
261415516c77SSepherosa Ziehau hn_link_status(struct hn_softc *sc)
261515516c77SSepherosa Ziehau {
261615516c77SSepherosa Ziehau 	uint32_t link_status;
261715516c77SSepherosa Ziehau 	int error;
261815516c77SSepherosa Ziehau 
261915516c77SSepherosa Ziehau 	error = hn_rndis_get_linkstatus(sc, &link_status);
262015516c77SSepherosa Ziehau 	if (error) {
262115516c77SSepherosa Ziehau 		/* XXX what to do? */
262215516c77SSepherosa Ziehau 		return;
262315516c77SSepherosa Ziehau 	}
262415516c77SSepherosa Ziehau 
262515516c77SSepherosa Ziehau 	if (link_status == NDIS_MEDIA_STATE_CONNECTED)
262615516c77SSepherosa Ziehau 		sc->hn_link_flags |= HN_LINK_FLAG_LINKUP;
262715516c77SSepherosa Ziehau 	else
262815516c77SSepherosa Ziehau 		sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
262915516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp,
263015516c77SSepherosa Ziehau 	    (sc->hn_link_flags & HN_LINK_FLAG_LINKUP) ?
263115516c77SSepherosa Ziehau 	    LINK_STATE_UP : LINK_STATE_DOWN);
263215516c77SSepherosa Ziehau }
263315516c77SSepherosa Ziehau 
263415516c77SSepherosa Ziehau static void
263515516c77SSepherosa Ziehau hn_link_taskfunc(void *xsc, int pending __unused)
263615516c77SSepherosa Ziehau {
263715516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
263815516c77SSepherosa Ziehau 
263915516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
264015516c77SSepherosa Ziehau 		return;
264115516c77SSepherosa Ziehau 	hn_link_status(sc);
264215516c77SSepherosa Ziehau }
264315516c77SSepherosa Ziehau 
264415516c77SSepherosa Ziehau static void
264515516c77SSepherosa Ziehau hn_netchg_init_taskfunc(void *xsc, int pending __unused)
264615516c77SSepherosa Ziehau {
264715516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
264815516c77SSepherosa Ziehau 
264915516c77SSepherosa Ziehau 	/* Prevent any link status checks from running. */
265015516c77SSepherosa Ziehau 	sc->hn_link_flags |= HN_LINK_FLAG_NETCHG;
265115516c77SSepherosa Ziehau 
265215516c77SSepherosa Ziehau 	/*
265315516c77SSepherosa Ziehau 	 * Fake up a [link down --> link up] state change; 5 seconds
265415516c77SSepherosa Ziehau 	 * delay is used, which closely simulates miibus reaction
265515516c77SSepherosa Ziehau 	 * upon link down event.
265615516c77SSepherosa Ziehau 	 */
265715516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
265815516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
265915516c77SSepherosa Ziehau 	taskqueue_enqueue_timeout(sc->hn_mgmt_taskq0,
266015516c77SSepherosa Ziehau 	    &sc->hn_netchg_status, 5 * hz);
266115516c77SSepherosa Ziehau }
266215516c77SSepherosa Ziehau 
266315516c77SSepherosa Ziehau static void
266415516c77SSepherosa Ziehau hn_netchg_status_taskfunc(void *xsc, int pending __unused)
266515516c77SSepherosa Ziehau {
266615516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
266715516c77SSepherosa Ziehau 
266815516c77SSepherosa Ziehau 	/* Re-allow link status checks. */
266915516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_NETCHG;
267015516c77SSepherosa Ziehau 	hn_link_status(sc);
267115516c77SSepherosa Ziehau }
267215516c77SSepherosa Ziehau 
267315516c77SSepherosa Ziehau static void
267415516c77SSepherosa Ziehau hn_update_link_status(struct hn_softc *sc)
267515516c77SSepherosa Ziehau {
267615516c77SSepherosa Ziehau 
267715516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
267815516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
267915516c77SSepherosa Ziehau }
268015516c77SSepherosa Ziehau 
268115516c77SSepherosa Ziehau static void
268215516c77SSepherosa Ziehau hn_change_network(struct hn_softc *sc)
268315516c77SSepherosa Ziehau {
268415516c77SSepherosa Ziehau 
268515516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
268615516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_netchg_init);
268715516c77SSepherosa Ziehau }
268815516c77SSepherosa Ziehau 
268915516c77SSepherosa Ziehau static __inline int
269015516c77SSepherosa Ziehau hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
269115516c77SSepherosa Ziehau     struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
269215516c77SSepherosa Ziehau {
269315516c77SSepherosa Ziehau 	struct mbuf *m = *m_head;
269415516c77SSepherosa Ziehau 	int error;
269515516c77SSepherosa Ziehau 
269615516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID, ("txd uses chim"));
269715516c77SSepherosa Ziehau 
269815516c77SSepherosa Ziehau 	error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag, txd->data_dmap,
269915516c77SSepherosa Ziehau 	    m, segs, nsegs, BUS_DMA_NOWAIT);
270015516c77SSepherosa Ziehau 	if (error == EFBIG) {
270115516c77SSepherosa Ziehau 		struct mbuf *m_new;
270215516c77SSepherosa Ziehau 
270315516c77SSepherosa Ziehau 		m_new = m_collapse(m, M_NOWAIT, HN_TX_DATA_SEGCNT_MAX);
270415516c77SSepherosa Ziehau 		if (m_new == NULL)
270515516c77SSepherosa Ziehau 			return ENOBUFS;
270615516c77SSepherosa Ziehau 		else
270715516c77SSepherosa Ziehau 			*m_head = m = m_new;
270815516c77SSepherosa Ziehau 		txr->hn_tx_collapsed++;
270915516c77SSepherosa Ziehau 
271015516c77SSepherosa Ziehau 		error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag,
271115516c77SSepherosa Ziehau 		    txd->data_dmap, m, segs, nsegs, BUS_DMA_NOWAIT);
271215516c77SSepherosa Ziehau 	}
271315516c77SSepherosa Ziehau 	if (!error) {
271415516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag, txd->data_dmap,
271515516c77SSepherosa Ziehau 		    BUS_DMASYNC_PREWRITE);
271615516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_DMAMAP;
271715516c77SSepherosa Ziehau 	}
271815516c77SSepherosa Ziehau 	return error;
271915516c77SSepherosa Ziehau }
272015516c77SSepherosa Ziehau 
272115516c77SSepherosa Ziehau static __inline int
272215516c77SSepherosa Ziehau hn_txdesc_put(struct hn_tx_ring *txr, struct hn_txdesc *txd)
272315516c77SSepherosa Ziehau {
272415516c77SSepherosa Ziehau 
272515516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONLIST) == 0,
272615516c77SSepherosa Ziehau 	    ("put an onlist txd %#x", txd->flags));
2727dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2728dc13fee6SSepherosa Ziehau 	    ("put an onagg txd %#x", txd->flags));
272915516c77SSepherosa Ziehau 
273015516c77SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
273115516c77SSepherosa Ziehau 	if (atomic_fetchadd_int(&txd->refs, -1) != 1)
273215516c77SSepherosa Ziehau 		return 0;
273315516c77SSepherosa Ziehau 
2734dc13fee6SSepherosa Ziehau 	if (!STAILQ_EMPTY(&txd->agg_list)) {
2735dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tmp_txd;
2736dc13fee6SSepherosa Ziehau 
2737dc13fee6SSepherosa Ziehau 		while ((tmp_txd = STAILQ_FIRST(&txd->agg_list)) != NULL) {
2738a0f49d67SMateusz Guzik 			int freed __diagused;
2739dc13fee6SSepherosa Ziehau 
2740dc13fee6SSepherosa Ziehau 			KASSERT(STAILQ_EMPTY(&tmp_txd->agg_list),
2741dc13fee6SSepherosa Ziehau 			    ("resursive aggregation on aggregated txdesc"));
2742dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_ONAGG),
2743dc13fee6SSepherosa Ziehau 			    ("not aggregated txdesc"));
2744dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
2745dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc uses dmamap"));
2746dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
2747dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc consumes "
2748dc13fee6SSepherosa Ziehau 			     "chimney sending buffer"));
2749dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_size == 0,
2750dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc has non-zero "
2751dc13fee6SSepherosa Ziehau 			     "chimney sending size"));
2752dc13fee6SSepherosa Ziehau 
2753dc13fee6SSepherosa Ziehau 			STAILQ_REMOVE_HEAD(&txd->agg_list, agg_link);
2754dc13fee6SSepherosa Ziehau 			tmp_txd->flags &= ~HN_TXD_FLAG_ONAGG;
2755dc13fee6SSepherosa Ziehau 			freed = hn_txdesc_put(txr, tmp_txd);
2756dc13fee6SSepherosa Ziehau 			KASSERT(freed, ("failed to free aggregated txdesc"));
2757dc13fee6SSepherosa Ziehau 		}
2758dc13fee6SSepherosa Ziehau 	}
2759dc13fee6SSepherosa Ziehau 
276015516c77SSepherosa Ziehau 	if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) {
276115516c77SSepherosa Ziehau 		KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
276215516c77SSepherosa Ziehau 		    ("chim txd uses dmamap"));
276315516c77SSepherosa Ziehau 		hn_chim_free(txr->hn_sc, txd->chim_index);
276415516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
2765dc13fee6SSepherosa Ziehau 		txd->chim_size = 0;
276615516c77SSepherosa Ziehau 	} else if (txd->flags & HN_TXD_FLAG_DMAMAP) {
276715516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag,
276815516c77SSepherosa Ziehau 		    txd->data_dmap, BUS_DMASYNC_POSTWRITE);
276915516c77SSepherosa Ziehau 		bus_dmamap_unload(txr->hn_tx_data_dtag,
277015516c77SSepherosa Ziehau 		    txd->data_dmap);
277115516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_DMAMAP;
277215516c77SSepherosa Ziehau 	}
277315516c77SSepherosa Ziehau 
277415516c77SSepherosa Ziehau 	if (txd->m != NULL) {
277515516c77SSepherosa Ziehau 		m_freem(txd->m);
277615516c77SSepherosa Ziehau 		txd->m = NULL;
277715516c77SSepherosa Ziehau 	}
277815516c77SSepherosa Ziehau 
277915516c77SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONLIST;
278015516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
278115516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
278215516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_avail >= 0 &&
278315516c77SSepherosa Ziehau 	    txr->hn_txdesc_avail < txr->hn_txdesc_cnt,
278415516c77SSepherosa Ziehau 	    ("txdesc_put: invalid txd avail %d", txr->hn_txdesc_avail));
278515516c77SSepherosa Ziehau 	txr->hn_txdesc_avail++;
278615516c77SSepherosa Ziehau 	SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
278715516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
278885e4ae1eSSepherosa Ziehau #else	/* HN_USE_TXDESC_BUFRING */
278985e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
279015516c77SSepherosa Ziehau 	atomic_add_int(&txr->hn_txdesc_avail, 1);
279115516c77SSepherosa Ziehau #endif
279285e4ae1eSSepherosa Ziehau 	buf_ring_enqueue(txr->hn_txdesc_br, txd);
279385e4ae1eSSepherosa Ziehau #endif	/* !HN_USE_TXDESC_BUFRING */
279415516c77SSepherosa Ziehau 
279515516c77SSepherosa Ziehau 	return 1;
279615516c77SSepherosa Ziehau }
279715516c77SSepherosa Ziehau 
279815516c77SSepherosa Ziehau static __inline struct hn_txdesc *
279915516c77SSepherosa Ziehau hn_txdesc_get(struct hn_tx_ring *txr)
280015516c77SSepherosa Ziehau {
280115516c77SSepherosa Ziehau 	struct hn_txdesc *txd;
280215516c77SSepherosa Ziehau 
280315516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
280415516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
280515516c77SSepherosa Ziehau 	txd = SLIST_FIRST(&txr->hn_txlist);
280615516c77SSepherosa Ziehau 	if (txd != NULL) {
280715516c77SSepherosa Ziehau 		KASSERT(txr->hn_txdesc_avail > 0,
280815516c77SSepherosa Ziehau 		    ("txdesc_get: invalid txd avail %d", txr->hn_txdesc_avail));
280915516c77SSepherosa Ziehau 		txr->hn_txdesc_avail--;
281015516c77SSepherosa Ziehau 		SLIST_REMOVE_HEAD(&txr->hn_txlist, link);
281115516c77SSepherosa Ziehau 	}
281215516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
281315516c77SSepherosa Ziehau #else
281415516c77SSepherosa Ziehau 	txd = buf_ring_dequeue_sc(txr->hn_txdesc_br);
281515516c77SSepherosa Ziehau #endif
281615516c77SSepherosa Ziehau 
281715516c77SSepherosa Ziehau 	if (txd != NULL) {
281815516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
281985e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
282015516c77SSepherosa Ziehau 		atomic_subtract_int(&txr->hn_txdesc_avail, 1);
282115516c77SSepherosa Ziehau #endif
282285e4ae1eSSepherosa Ziehau #endif	/* HN_USE_TXDESC_BUFRING */
282315516c77SSepherosa Ziehau 		KASSERT(txd->m == NULL && txd->refs == 0 &&
2824dc13fee6SSepherosa Ziehau 		    STAILQ_EMPTY(&txd->agg_list) &&
282515516c77SSepherosa Ziehau 		    txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
2826dc13fee6SSepherosa Ziehau 		    txd->chim_size == 0 &&
282715516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONLIST) &&
2828dc13fee6SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONAGG) == 0 &&
282915516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("invalid txd"));
283015516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_ONLIST;
283115516c77SSepherosa Ziehau 		txd->refs = 1;
283215516c77SSepherosa Ziehau 	}
283315516c77SSepherosa Ziehau 	return txd;
283415516c77SSepherosa Ziehau }
283515516c77SSepherosa Ziehau 
283615516c77SSepherosa Ziehau static __inline void
283715516c77SSepherosa Ziehau hn_txdesc_hold(struct hn_txdesc *txd)
283815516c77SSepherosa Ziehau {
283915516c77SSepherosa Ziehau 
284015516c77SSepherosa Ziehau 	/* 0->1 transition will never work */
284125641fc7SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
284215516c77SSepherosa Ziehau 	atomic_add_int(&txd->refs, 1);
284315516c77SSepherosa Ziehau }
284415516c77SSepherosa Ziehau 
2845dc13fee6SSepherosa Ziehau static __inline void
2846dc13fee6SSepherosa Ziehau hn_txdesc_agg(struct hn_txdesc *agg_txd, struct hn_txdesc *txd)
2847dc13fee6SSepherosa Ziehau {
2848dc13fee6SSepherosa Ziehau 
2849dc13fee6SSepherosa Ziehau 	KASSERT((agg_txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2850dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on aggregating txdesc"));
2851dc13fee6SSepherosa Ziehau 
2852dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2853dc13fee6SSepherosa Ziehau 	    ("already aggregated"));
2854dc13fee6SSepherosa Ziehau 	KASSERT(STAILQ_EMPTY(&txd->agg_list),
2855dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on to-be-aggregated txdesc"));
2856dc13fee6SSepherosa Ziehau 
2857dc13fee6SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONAGG;
2858dc13fee6SSepherosa Ziehau 	STAILQ_INSERT_TAIL(&agg_txd->agg_list, txd, agg_link);
2859dc13fee6SSepherosa Ziehau }
2860dc13fee6SSepherosa Ziehau 
286115516c77SSepherosa Ziehau static bool
286215516c77SSepherosa Ziehau hn_tx_ring_pending(struct hn_tx_ring *txr)
286315516c77SSepherosa Ziehau {
286415516c77SSepherosa Ziehau 	bool pending = false;
286515516c77SSepherosa Ziehau 
286615516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
286715516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
286815516c77SSepherosa Ziehau 	if (txr->hn_txdesc_avail != txr->hn_txdesc_cnt)
286915516c77SSepherosa Ziehau 		pending = true;
287015516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
287115516c77SSepherosa Ziehau #else
287215516c77SSepherosa Ziehau 	if (!buf_ring_full(txr->hn_txdesc_br))
287315516c77SSepherosa Ziehau 		pending = true;
287415516c77SSepherosa Ziehau #endif
287515516c77SSepherosa Ziehau 	return (pending);
287615516c77SSepherosa Ziehau }
287715516c77SSepherosa Ziehau 
287815516c77SSepherosa Ziehau static __inline void
287915516c77SSepherosa Ziehau hn_txeof(struct hn_tx_ring *txr)
288015516c77SSepherosa Ziehau {
288115516c77SSepherosa Ziehau 	txr->hn_has_txeof = 0;
288215516c77SSepherosa Ziehau 	txr->hn_txeof(txr);
288315516c77SSepherosa Ziehau }
288415516c77SSepherosa Ziehau 
288515516c77SSepherosa Ziehau static void
288615516c77SSepherosa Ziehau hn_txpkt_done(struct hn_nvs_sendctx *sndc, struct hn_softc *sc,
288715516c77SSepherosa Ziehau     struct vmbus_channel *chan, const void *data __unused, int dlen __unused)
288815516c77SSepherosa Ziehau {
288915516c77SSepherosa Ziehau 	struct hn_txdesc *txd = sndc->hn_cbarg;
289015516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
289115516c77SSepherosa Ziehau 
289215516c77SSepherosa Ziehau 	txr = txd->txr;
289315516c77SSepherosa Ziehau 	KASSERT(txr->hn_chan == chan,
289415516c77SSepherosa Ziehau 	    ("channel mismatch, on chan%u, should be chan%u",
2895aa1a2adcSSepherosa Ziehau 	     vmbus_chan_id(chan), vmbus_chan_id(txr->hn_chan)));
289615516c77SSepherosa Ziehau 
289715516c77SSepherosa Ziehau 	txr->hn_has_txeof = 1;
289815516c77SSepherosa Ziehau 	hn_txdesc_put(txr, txd);
289915516c77SSepherosa Ziehau 
290015516c77SSepherosa Ziehau 	++txr->hn_txdone_cnt;
290115516c77SSepherosa Ziehau 	if (txr->hn_txdone_cnt >= HN_EARLY_TXEOF_THRESH) {
290215516c77SSepherosa Ziehau 		txr->hn_txdone_cnt = 0;
290315516c77SSepherosa Ziehau 		if (txr->hn_oactive)
290415516c77SSepherosa Ziehau 			hn_txeof(txr);
290515516c77SSepherosa Ziehau 	}
290615516c77SSepherosa Ziehau }
290715516c77SSepherosa Ziehau 
290815516c77SSepherosa Ziehau static void
290915516c77SSepherosa Ziehau hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
291015516c77SSepherosa Ziehau {
291115516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
291226d79d40SMichael Tuexen 	struct epoch_tracker et;
291326d79d40SMichael Tuexen 
291426d79d40SMichael Tuexen 	NET_EPOCH_ENTER(et);
291515516c77SSepherosa Ziehau 	tcp_lro_flush_all(&rxr->hn_lro);
291626d79d40SMichael Tuexen 	NET_EPOCH_EXIT(et);
291715516c77SSepherosa Ziehau #endif
291815516c77SSepherosa Ziehau 
291915516c77SSepherosa Ziehau 	/*
292015516c77SSepherosa Ziehau 	 * NOTE:
292115516c77SSepherosa Ziehau 	 * 'txr' could be NULL, if multiple channels and
292215516c77SSepherosa Ziehau 	 * ifnet.if_start method are enabled.
292315516c77SSepherosa Ziehau 	 */
292415516c77SSepherosa Ziehau 	if (txr == NULL || !txr->hn_has_txeof)
292515516c77SSepherosa Ziehau 		return;
292615516c77SSepherosa Ziehau 
292715516c77SSepherosa Ziehau 	txr->hn_txdone_cnt = 0;
292815516c77SSepherosa Ziehau 	hn_txeof(txr);
292915516c77SSepherosa Ziehau }
293015516c77SSepherosa Ziehau 
293115516c77SSepherosa Ziehau static __inline uint32_t
293215516c77SSepherosa Ziehau hn_rndis_pktmsg_offset(uint32_t ofs)
293315516c77SSepherosa Ziehau {
293415516c77SSepherosa Ziehau 
293515516c77SSepherosa Ziehau 	KASSERT(ofs >= sizeof(struct rndis_packet_msg),
293615516c77SSepherosa Ziehau 	    ("invalid RNDIS packet msg offset %u", ofs));
293715516c77SSepherosa Ziehau 	return (ofs - __offsetof(struct rndis_packet_msg, rm_dataoffset));
293815516c77SSepherosa Ziehau }
293915516c77SSepherosa Ziehau 
294015516c77SSepherosa Ziehau static __inline void *
294115516c77SSepherosa Ziehau hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
294215516c77SSepherosa Ziehau     size_t pi_dlen, uint32_t pi_type)
294315516c77SSepherosa Ziehau {
294415516c77SSepherosa Ziehau 	const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
294515516c77SSepherosa Ziehau 	struct rndis_pktinfo *pi;
294615516c77SSepherosa Ziehau 
294715516c77SSepherosa Ziehau 	KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
294815516c77SSepherosa Ziehau 	    ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
294915516c77SSepherosa Ziehau 
295015516c77SSepherosa Ziehau 	/*
295115516c77SSepherosa Ziehau 	 * Per-packet-info does not move; it only grows.
295215516c77SSepherosa Ziehau 	 *
295315516c77SSepherosa Ziehau 	 * NOTE:
295415516c77SSepherosa Ziehau 	 * rm_pktinfooffset in this phase counts from the beginning
295515516c77SSepherosa Ziehau 	 * of rndis_packet_msg.
295615516c77SSepherosa Ziehau 	 */
295715516c77SSepherosa Ziehau 	KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
295815516c77SSepherosa Ziehau 	    ("%u pktinfo overflows RNDIS packet msg", pi_type));
295915516c77SSepherosa Ziehau 	pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
296015516c77SSepherosa Ziehau 	    pkt->rm_pktinfolen);
296115516c77SSepherosa Ziehau 	pkt->rm_pktinfolen += pi_size;
296215516c77SSepherosa Ziehau 
296315516c77SSepherosa Ziehau 	pi->rm_size = pi_size;
296415516c77SSepherosa Ziehau 	pi->rm_type = pi_type;
2965805dbff6SWei Hu 	pi->rm_internal = 0;
296615516c77SSepherosa Ziehau 	pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
296715516c77SSepherosa Ziehau 
296815516c77SSepherosa Ziehau 	return (pi->rm_data);
296915516c77SSepherosa Ziehau }
297015516c77SSepherosa Ziehau 
2971dc13fee6SSepherosa Ziehau static __inline int
2972*4db5958aSJustin Hibbits hn_flush_txagg(if_t ifp, struct hn_tx_ring *txr)
2973dc13fee6SSepherosa Ziehau {
2974dc13fee6SSepherosa Ziehau 	struct hn_txdesc *txd;
2975dc13fee6SSepherosa Ziehau 	struct mbuf *m;
2976dc13fee6SSepherosa Ziehau 	int error, pkts;
2977dc13fee6SSepherosa Ziehau 
2978dc13fee6SSepherosa Ziehau 	txd = txr->hn_agg_txd;
2979dc13fee6SSepherosa Ziehau 	KASSERT(txd != NULL, ("no aggregate txdesc"));
2980dc13fee6SSepherosa Ziehau 
2981dc13fee6SSepherosa Ziehau 	/*
2982dc13fee6SSepherosa Ziehau 	 * Since hn_txpkt() will reset this temporary stat, save
2983dc13fee6SSepherosa Ziehau 	 * it now, so that oerrors can be updated properly, if
2984dc13fee6SSepherosa Ziehau 	 * hn_txpkt() ever fails.
2985dc13fee6SSepherosa Ziehau 	 */
2986dc13fee6SSepherosa Ziehau 	pkts = txr->hn_stat_pkts;
2987dc13fee6SSepherosa Ziehau 
2988dc13fee6SSepherosa Ziehau 	/*
2989dc13fee6SSepherosa Ziehau 	 * Since txd's mbuf will _not_ be freed upon hn_txpkt()
2990dc13fee6SSepherosa Ziehau 	 * failure, save it for later freeing, if hn_txpkt() ever
2991dc13fee6SSepherosa Ziehau 	 * fails.
2992dc13fee6SSepherosa Ziehau 	 */
2993dc13fee6SSepherosa Ziehau 	m = txd->m;
2994dc13fee6SSepherosa Ziehau 	error = hn_txpkt(ifp, txr, txd);
2995dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
2996dc13fee6SSepherosa Ziehau 		/* txd is freed, but m is not. */
2997dc13fee6SSepherosa Ziehau 		m_freem(m);
2998dc13fee6SSepherosa Ziehau 
2999dc13fee6SSepherosa Ziehau 		txr->hn_flush_failed++;
3000dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, pkts);
3001dc13fee6SSepherosa Ziehau 	}
3002dc13fee6SSepherosa Ziehau 
3003dc13fee6SSepherosa Ziehau 	/* Reset all aggregation states. */
3004dc13fee6SSepherosa Ziehau 	txr->hn_agg_txd = NULL;
3005dc13fee6SSepherosa Ziehau 	txr->hn_agg_szleft = 0;
3006dc13fee6SSepherosa Ziehau 	txr->hn_agg_pktleft = 0;
3007dc13fee6SSepherosa Ziehau 	txr->hn_agg_prevpkt = NULL;
3008dc13fee6SSepherosa Ziehau 
3009dc13fee6SSepherosa Ziehau 	return (error);
3010dc13fee6SSepherosa Ziehau }
3011dc13fee6SSepherosa Ziehau 
3012dc13fee6SSepherosa Ziehau static void *
3013*4db5958aSJustin Hibbits hn_try_txagg(if_t ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
3014dc13fee6SSepherosa Ziehau     int pktsize)
3015dc13fee6SSepherosa Ziehau {
3016dc13fee6SSepherosa Ziehau 	void *chim;
3017dc13fee6SSepherosa Ziehau 
3018dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL) {
3019dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft >= 1 && txr->hn_agg_szleft > pktsize) {
3020dc13fee6SSepherosa Ziehau 			struct hn_txdesc *agg_txd = txr->hn_agg_txd;
3021dc13fee6SSepherosa Ziehau 			struct rndis_packet_msg *pkt = txr->hn_agg_prevpkt;
3022dc13fee6SSepherosa Ziehau 			int olen;
3023dc13fee6SSepherosa Ziehau 
3024dc13fee6SSepherosa Ziehau 			/*
3025dc13fee6SSepherosa Ziehau 			 * Update the previous RNDIS packet's total length,
3026dc13fee6SSepherosa Ziehau 			 * it can be increased due to the mandatory alignment
3027dc13fee6SSepherosa Ziehau 			 * padding for this RNDIS packet.  And update the
3028dc13fee6SSepherosa Ziehau 			 * aggregating txdesc's chimney sending buffer size
3029dc13fee6SSepherosa Ziehau 			 * accordingly.
3030dc13fee6SSepherosa Ziehau 			 *
3031dc13fee6SSepherosa Ziehau 			 * XXX
3032dc13fee6SSepherosa Ziehau 			 * Zero-out the padding, as required by the RNDIS spec.
3033dc13fee6SSepherosa Ziehau 			 */
3034dc13fee6SSepherosa Ziehau 			olen = pkt->rm_len;
3035dc13fee6SSepherosa Ziehau 			pkt->rm_len = roundup2(olen, txr->hn_agg_align);
3036dc13fee6SSepherosa Ziehau 			agg_txd->chim_size += pkt->rm_len - olen;
3037dc13fee6SSepherosa Ziehau 
3038dc13fee6SSepherosa Ziehau 			/* Link this txdesc to the parent. */
3039dc13fee6SSepherosa Ziehau 			hn_txdesc_agg(agg_txd, txd);
3040dc13fee6SSepherosa Ziehau 
3041dc13fee6SSepherosa Ziehau 			chim = (uint8_t *)pkt + pkt->rm_len;
3042dc13fee6SSepherosa Ziehau 			/* Save the current packet for later fixup. */
3043dc13fee6SSepherosa Ziehau 			txr->hn_agg_prevpkt = chim;
3044dc13fee6SSepherosa Ziehau 
3045dc13fee6SSepherosa Ziehau 			txr->hn_agg_pktleft--;
3046dc13fee6SSepherosa Ziehau 			txr->hn_agg_szleft -= pktsize;
3047dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_szleft <=
3048dc13fee6SSepherosa Ziehau 			    HN_PKTSIZE_MIN(txr->hn_agg_align)) {
3049dc13fee6SSepherosa Ziehau 				/*
3050dc13fee6SSepherosa Ziehau 				 * Probably can't aggregate more packets,
3051dc13fee6SSepherosa Ziehau 				 * flush this aggregating txdesc proactively.
3052dc13fee6SSepherosa Ziehau 				 */
3053dc13fee6SSepherosa Ziehau 				txr->hn_agg_pktleft = 0;
3054dc13fee6SSepherosa Ziehau 			}
3055dc13fee6SSepherosa Ziehau 			/* Done! */
3056dc13fee6SSepherosa Ziehau 			return (chim);
3057dc13fee6SSepherosa Ziehau 		}
3058dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
3059dc13fee6SSepherosa Ziehau 	}
3060dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
3061dc13fee6SSepherosa Ziehau 
3062dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney_tried++;
3063dc13fee6SSepherosa Ziehau 	txd->chim_index = hn_chim_alloc(txr->hn_sc);
3064dc13fee6SSepherosa Ziehau 	if (txd->chim_index == HN_NVS_CHIM_IDX_INVALID)
3065dc13fee6SSepherosa Ziehau 		return (NULL);
3066dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney++;
3067dc13fee6SSepherosa Ziehau 
3068dc13fee6SSepherosa Ziehau 	chim = txr->hn_sc->hn_chim +
3069dc13fee6SSepherosa Ziehau 	    (txd->chim_index * txr->hn_sc->hn_chim_szmax);
3070dc13fee6SSepherosa Ziehau 
3071dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_pktmax > 1 &&
3072dc13fee6SSepherosa Ziehau 	    txr->hn_agg_szmax > pktsize + HN_PKTSIZE_MIN(txr->hn_agg_align)) {
3073dc13fee6SSepherosa Ziehau 		txr->hn_agg_txd = txd;
3074dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktleft = txr->hn_agg_pktmax - 1;
3075dc13fee6SSepherosa Ziehau 		txr->hn_agg_szleft = txr->hn_agg_szmax - pktsize;
3076dc13fee6SSepherosa Ziehau 		txr->hn_agg_prevpkt = chim;
3077dc13fee6SSepherosa Ziehau 	}
3078dc13fee6SSepherosa Ziehau 	return (chim);
3079dc13fee6SSepherosa Ziehau }
3080dc13fee6SSepherosa Ziehau 
308115516c77SSepherosa Ziehau /*
308215516c77SSepherosa Ziehau  * NOTE:
308315516c77SSepherosa Ziehau  * If this function fails, then both txd and m_head0 will be freed.
308415516c77SSepherosa Ziehau  */
308515516c77SSepherosa Ziehau static int
3086*4db5958aSJustin Hibbits hn_encap(if_t ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
3087dc13fee6SSepherosa Ziehau     struct mbuf **m_head0)
308815516c77SSepherosa Ziehau {
308915516c77SSepherosa Ziehau 	bus_dma_segment_t segs[HN_TX_DATA_SEGCNT_MAX];
309015516c77SSepherosa Ziehau 	int error, nsegs, i;
309115516c77SSepherosa Ziehau 	struct mbuf *m_head = *m_head0;
309215516c77SSepherosa Ziehau 	struct rndis_packet_msg *pkt;
309315516c77SSepherosa Ziehau 	uint32_t *pi_data;
30948966e5d5SSepherosa Ziehau 	void *chim = NULL;
3095dc13fee6SSepherosa Ziehau 	int pkt_hlen, pkt_size;
309615516c77SSepherosa Ziehau 
309715516c77SSepherosa Ziehau 	pkt = txd->rndis_pkt;
3098dc13fee6SSepherosa Ziehau 	pkt_size = HN_PKTSIZE(m_head, txr->hn_agg_align);
3099dc13fee6SSepherosa Ziehau 	if (pkt_size < txr->hn_chim_size) {
3100dc13fee6SSepherosa Ziehau 		chim = hn_try_txagg(ifp, txr, txd, pkt_size);
3101dc13fee6SSepherosa Ziehau 		if (chim != NULL)
31028966e5d5SSepherosa Ziehau 			pkt = chim;
3103dc13fee6SSepherosa Ziehau 	} else {
3104dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL)
3105dc13fee6SSepherosa Ziehau 			hn_flush_txagg(ifp, txr);
31068966e5d5SSepherosa Ziehau 	}
31078966e5d5SSepherosa Ziehau 
310815516c77SSepherosa Ziehau 	pkt->rm_type = REMOTE_NDIS_PACKET_MSG;
31098fe90f73SSepherosa Ziehau 	pkt->rm_len = m_head->m_pkthdr.len;
31109130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = 0;
311115516c77SSepherosa Ziehau 	pkt->rm_datalen = m_head->m_pkthdr.len;
3112dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataoffset = 0;
3113dc13fee6SSepherosa Ziehau 	pkt->rm_oobdatalen = 0;
3114dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataelements = 0;
311515516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = sizeof(*pkt);
311615516c77SSepherosa Ziehau 	pkt->rm_pktinfolen = 0;
3117dc13fee6SSepherosa Ziehau 	pkt->rm_vchandle = 0;
3118dc13fee6SSepherosa Ziehau 	pkt->rm_reserved = 0;
311915516c77SSepherosa Ziehau 
312015516c77SSepherosa Ziehau 	if (txr->hn_tx_flags & HN_TX_FLAG_HASHVAL) {
312115516c77SSepherosa Ziehau 		/*
312280f39bd9SWei Hu 		 * Set the hash value for this packet.
312315516c77SSepherosa Ziehau 		 */
312415516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
312515516c77SSepherosa Ziehau 		    HN_NDIS_HASH_VALUE_SIZE, HN_NDIS_PKTINFO_TYPE_HASHVAL);
312680f39bd9SWei Hu 
312780f39bd9SWei Hu 		if (M_HASHTYPE_ISHASH(m_head))
312880f39bd9SWei Hu 			/*
312980f39bd9SWei Hu 			 * The flowid field contains the hash value host
313080f39bd9SWei Hu 			 * set in the rx queue if it is a ip forwarding pkt.
313180f39bd9SWei Hu 			 * Set the same hash value so host can send on the
313280f39bd9SWei Hu 			 * cpu it was received.
313380f39bd9SWei Hu 			 */
313480f39bd9SWei Hu 			*pi_data = m_head->m_pkthdr.flowid;
313580f39bd9SWei Hu 		else
313680f39bd9SWei Hu 			/*
313780f39bd9SWei Hu 			 * Otherwise just put the tx queue index.
313880f39bd9SWei Hu 			 */
313915516c77SSepherosa Ziehau 			*pi_data = txr->hn_tx_idx;
314015516c77SSepherosa Ziehau 	}
314115516c77SSepherosa Ziehau 
314215516c77SSepherosa Ziehau 	if (m_head->m_flags & M_VLANTAG) {
314315516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
314415516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_SIZE, NDIS_PKTINFO_TYPE_VLAN);
314515516c77SSepherosa Ziehau 		*pi_data = NDIS_VLAN_INFO_MAKE(
314615516c77SSepherosa Ziehau 		    EVL_VLANOFTAG(m_head->m_pkthdr.ether_vtag),
314715516c77SSepherosa Ziehau 		    EVL_PRIOFTAG(m_head->m_pkthdr.ether_vtag),
314815516c77SSepherosa Ziehau 		    EVL_CFIOFTAG(m_head->m_pkthdr.ether_vtag));
314915516c77SSepherosa Ziehau 	}
315015516c77SSepherosa Ziehau 
315115516c77SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
315215516c77SSepherosa Ziehau #if defined(INET6) || defined(INET)
315315516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
315415516c77SSepherosa Ziehau 		    NDIS_LSO2_INFO_SIZE, NDIS_PKTINFO_TYPE_LSO);
315515516c77SSepherosa Ziehau #ifdef INET
315615516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
3157c49d47daSSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV4(
3158c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen,
315915516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
316015516c77SSepherosa Ziehau 		}
316115516c77SSepherosa Ziehau #endif
316215516c77SSepherosa Ziehau #if defined(INET6) && defined(INET)
316315516c77SSepherosa Ziehau 		else
316415516c77SSepherosa Ziehau #endif
316515516c77SSepherosa Ziehau #ifdef INET6
316615516c77SSepherosa Ziehau 		{
3167c49d47daSSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV6(
3168c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen,
316915516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
317015516c77SSepherosa Ziehau 		}
317115516c77SSepherosa Ziehau #endif
317215516c77SSepherosa Ziehau #endif	/* INET6 || INET */
317315516c77SSepherosa Ziehau 	} else if (m_head->m_pkthdr.csum_flags & txr->hn_csum_assist) {
317415516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
317515516c77SSepherosa Ziehau 		    NDIS_TXCSUM_INFO_SIZE, NDIS_PKTINFO_TYPE_CSUM);
317615516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
317715516c77SSepherosa Ziehau 		    (CSUM_IP6_TCP | CSUM_IP6_UDP)) {
317815516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV6;
317915516c77SSepherosa Ziehau 		} else {
318015516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV4;
318115516c77SSepherosa Ziehau 			if (m_head->m_pkthdr.csum_flags & CSUM_IP)
318215516c77SSepherosa Ziehau 				*pi_data |= NDIS_TXCSUM_INFO_IPCS;
318315516c77SSepherosa Ziehau 		}
318415516c77SSepherosa Ziehau 
3185c49d47daSSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
3186c49d47daSSepherosa Ziehau 		    (CSUM_IP_TCP | CSUM_IP6_TCP)) {
3187c49d47daSSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_MKTCPCS(
3188c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen);
3189c49d47daSSepherosa Ziehau 		} else if (m_head->m_pkthdr.csum_flags &
3190c49d47daSSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP6_UDP)) {
3191c49d47daSSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_MKUDPCS(
3192c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen);
3193c49d47daSSepherosa Ziehau 		}
319415516c77SSepherosa Ziehau 	}
319515516c77SSepherosa Ziehau 
3196dc13fee6SSepherosa Ziehau 	pkt_hlen = pkt->rm_pktinfooffset + pkt->rm_pktinfolen;
31978fe90f73SSepherosa Ziehau 	/* Fixup RNDIS packet message total length */
31988fe90f73SSepherosa Ziehau 	pkt->rm_len += pkt_hlen;
319915516c77SSepherosa Ziehau 	/* Convert RNDIS packet message offsets */
32009130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = hn_rndis_pktmsg_offset(pkt_hlen);
320115516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = hn_rndis_pktmsg_offset(pkt->rm_pktinfooffset);
320215516c77SSepherosa Ziehau 
320315516c77SSepherosa Ziehau 	/*
32048966e5d5SSepherosa Ziehau 	 * Fast path: Chimney sending.
320515516c77SSepherosa Ziehau 	 */
32068966e5d5SSepherosa Ziehau 	if (chim != NULL) {
3207dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tgt_txd = txd;
3208dc13fee6SSepherosa Ziehau 
3209dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL) {
3210dc13fee6SSepherosa Ziehau 			tgt_txd = txr->hn_agg_txd;
3211dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
3212dc13fee6SSepherosa Ziehau 			*m_head0 = NULL;
3213dc13fee6SSepherosa Ziehau #endif
3214dc13fee6SSepherosa Ziehau 		}
3215dc13fee6SSepherosa Ziehau 
3216dc13fee6SSepherosa Ziehau 		KASSERT(pkt == chim,
3217dc13fee6SSepherosa Ziehau 		    ("RNDIS pkt not in chimney sending buffer"));
3218dc13fee6SSepherosa Ziehau 		KASSERT(tgt_txd->chim_index != HN_NVS_CHIM_IDX_INVALID,
3219dc13fee6SSepherosa Ziehau 		    ("chimney sending buffer is not used"));
3220dc13fee6SSepherosa Ziehau 		tgt_txd->chim_size += pkt->rm_len;
322115516c77SSepherosa Ziehau 
32228966e5d5SSepherosa Ziehau 		m_copydata(m_head, 0, m_head->m_pkthdr.len,
3223dc13fee6SSepherosa Ziehau 		    ((uint8_t *)chim) + pkt_hlen);
322415516c77SSepherosa Ziehau 
322515516c77SSepherosa Ziehau 		txr->hn_gpa_cnt = 0;
322615516c77SSepherosa Ziehau 		txr->hn_sendpkt = hn_txpkt_chim;
322715516c77SSepherosa Ziehau 		goto done;
322815516c77SSepherosa Ziehau 	}
3229dc13fee6SSepherosa Ziehau 
3230dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("aggregating sglist txdesc"));
32318966e5d5SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
32328966e5d5SSepherosa Ziehau 	    ("chimney buffer is used"));
32338966e5d5SSepherosa Ziehau 	KASSERT(pkt == txd->rndis_pkt, ("RNDIS pkt not in txdesc"));
323415516c77SSepherosa Ziehau 
323515516c77SSepherosa Ziehau 	error = hn_txdesc_dmamap_load(txr, txd, &m_head, segs, &nsegs);
3236dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
3237a0f49d67SMateusz Guzik 		int freed __diagused;
323815516c77SSepherosa Ziehau 
323915516c77SSepherosa Ziehau 		/*
324015516c77SSepherosa Ziehau 		 * This mbuf is not linked w/ the txd yet, so free it now.
324115516c77SSepherosa Ziehau 		 */
324215516c77SSepherosa Ziehau 		m_freem(m_head);
324315516c77SSepherosa Ziehau 		*m_head0 = NULL;
324415516c77SSepherosa Ziehau 
324515516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
324615516c77SSepherosa Ziehau 		KASSERT(freed != 0,
324715516c77SSepherosa Ziehau 		    ("fail to free txd upon txdma error"));
324815516c77SSepherosa Ziehau 
324915516c77SSepherosa Ziehau 		txr->hn_txdma_failed++;
3250dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
325115516c77SSepherosa Ziehau 		return error;
325215516c77SSepherosa Ziehau 	}
325315516c77SSepherosa Ziehau 	*m_head0 = m_head;
325415516c77SSepherosa Ziehau 
325515516c77SSepherosa Ziehau 	/* +1 RNDIS packet message */
325615516c77SSepherosa Ziehau 	txr->hn_gpa_cnt = nsegs + 1;
325715516c77SSepherosa Ziehau 
325815516c77SSepherosa Ziehau 	/* send packet with page buffer */
325915516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_page = atop(txd->rndis_pkt_paddr);
326015516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_ofs = txd->rndis_pkt_paddr & PAGE_MASK;
3261dc13fee6SSepherosa Ziehau 	txr->hn_gpa[0].gpa_len = pkt_hlen;
326215516c77SSepherosa Ziehau 
326315516c77SSepherosa Ziehau 	/*
326415516c77SSepherosa Ziehau 	 * Fill the page buffers with mbuf info after the page
326515516c77SSepherosa Ziehau 	 * buffer for RNDIS packet message.
326615516c77SSepherosa Ziehau 	 */
326715516c77SSepherosa Ziehau 	for (i = 0; i < nsegs; ++i) {
326815516c77SSepherosa Ziehau 		struct vmbus_gpa *gpa = &txr->hn_gpa[i + 1];
326915516c77SSepherosa Ziehau 
327015516c77SSepherosa Ziehau 		gpa->gpa_page = atop(segs[i].ds_addr);
327115516c77SSepherosa Ziehau 		gpa->gpa_ofs = segs[i].ds_addr & PAGE_MASK;
327215516c77SSepherosa Ziehau 		gpa->gpa_len = segs[i].ds_len;
327315516c77SSepherosa Ziehau 	}
327415516c77SSepherosa Ziehau 
327515516c77SSepherosa Ziehau 	txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
327615516c77SSepherosa Ziehau 	txd->chim_size = 0;
327715516c77SSepherosa Ziehau 	txr->hn_sendpkt = hn_txpkt_sglist;
327815516c77SSepherosa Ziehau done:
327915516c77SSepherosa Ziehau 	txd->m = m_head;
328015516c77SSepherosa Ziehau 
328115516c77SSepherosa Ziehau 	/* Set the completion routine */
328215516c77SSepherosa Ziehau 	hn_nvs_sendctx_init(&txd->send_ctx, hn_txpkt_done, txd);
328315516c77SSepherosa Ziehau 
3284dc13fee6SSepherosa Ziehau 	/* Update temporary stats for later use. */
3285dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts++;
3286dc13fee6SSepherosa Ziehau 	txr->hn_stat_size += m_head->m_pkthdr.len;
3287dc13fee6SSepherosa Ziehau 	if (m_head->m_flags & M_MCAST)
3288dc13fee6SSepherosa Ziehau 		txr->hn_stat_mcasts++;
3289dc13fee6SSepherosa Ziehau 
329015516c77SSepherosa Ziehau 	return 0;
329115516c77SSepherosa Ziehau }
329215516c77SSepherosa Ziehau 
329315516c77SSepherosa Ziehau /*
329415516c77SSepherosa Ziehau  * NOTE:
329515516c77SSepherosa Ziehau  * If this function fails, then txd will be freed, but the mbuf
329615516c77SSepherosa Ziehau  * associated w/ the txd will _not_ be freed.
329715516c77SSepherosa Ziehau  */
329815516c77SSepherosa Ziehau static int
3299*4db5958aSJustin Hibbits hn_txpkt(if_t ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd)
330015516c77SSepherosa Ziehau {
33018e7d3136SSepherosa Ziehau 	int error, send_failed = 0, has_bpf;
330215516c77SSepherosa Ziehau 
330315516c77SSepherosa Ziehau again:
3304*4db5958aSJustin Hibbits 	has_bpf = bpf_peers_present(if_getbpf(ifp));
33058e7d3136SSepherosa Ziehau 	if (has_bpf) {
330615516c77SSepherosa Ziehau 		/*
33078e7d3136SSepherosa Ziehau 		 * Make sure that this txd and any aggregated txds are not
33088e7d3136SSepherosa Ziehau 		 * freed before ETHER_BPF_MTAP.
330915516c77SSepherosa Ziehau 		 */
331015516c77SSepherosa Ziehau 		hn_txdesc_hold(txd);
33118e7d3136SSepherosa Ziehau 	}
331215516c77SSepherosa Ziehau 	error = txr->hn_sendpkt(txr, txd);
331315516c77SSepherosa Ziehau 	if (!error) {
33148e7d3136SSepherosa Ziehau 		if (has_bpf) {
3315dc13fee6SSepherosa Ziehau 			const struct hn_txdesc *tmp_txd;
3316dc13fee6SSepherosa Ziehau 
331715516c77SSepherosa Ziehau 			ETHER_BPF_MTAP(ifp, txd->m);
3318dc13fee6SSepherosa Ziehau 			STAILQ_FOREACH(tmp_txd, &txd->agg_list, agg_link)
3319dc13fee6SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, tmp_txd->m);
3320dc13fee6SSepherosa Ziehau 		}
3321dc13fee6SSepherosa Ziehau 
3322dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, txr->hn_stat_pkts);
332323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
332423bf9e15SSepherosa Ziehau 		if (!hn_use_if_start)
332523bf9e15SSepherosa Ziehau #endif
332623bf9e15SSepherosa Ziehau 		{
332715516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OBYTES,
3328dc13fee6SSepherosa Ziehau 			    txr->hn_stat_size);
3329dc13fee6SSepherosa Ziehau 			if (txr->hn_stat_mcasts != 0) {
3330dc13fee6SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OMCASTS,
3331dc13fee6SSepherosa Ziehau 				    txr->hn_stat_mcasts);
333215516c77SSepherosa Ziehau 			}
3333dc13fee6SSepherosa Ziehau 		}
3334dc13fee6SSepherosa Ziehau 		txr->hn_pkts += txr->hn_stat_pkts;
3335dc13fee6SSepherosa Ziehau 		txr->hn_sends++;
333615516c77SSepherosa Ziehau 	}
33378e7d3136SSepherosa Ziehau 	if (has_bpf)
333815516c77SSepherosa Ziehau 		hn_txdesc_put(txr, txd);
333915516c77SSepherosa Ziehau 
334015516c77SSepherosa Ziehau 	if (__predict_false(error)) {
3341a0f49d67SMateusz Guzik 		int freed __diagused;
334215516c77SSepherosa Ziehau 
334315516c77SSepherosa Ziehau 		/*
334415516c77SSepherosa Ziehau 		 * This should "really rarely" happen.
334515516c77SSepherosa Ziehau 		 *
334615516c77SSepherosa Ziehau 		 * XXX Too many RX to be acked or too many sideband
334715516c77SSepherosa Ziehau 		 * commands to run?  Ask netvsc_channel_rollup()
334815516c77SSepherosa Ziehau 		 * to kick start later.
334915516c77SSepherosa Ziehau 		 */
335015516c77SSepherosa Ziehau 		txr->hn_has_txeof = 1;
335115516c77SSepherosa Ziehau 		if (!send_failed) {
335215516c77SSepherosa Ziehau 			txr->hn_send_failed++;
335315516c77SSepherosa Ziehau 			send_failed = 1;
335415516c77SSepherosa Ziehau 			/*
335515516c77SSepherosa Ziehau 			 * Try sending again after set hn_has_txeof;
335615516c77SSepherosa Ziehau 			 * in case that we missed the last
335715516c77SSepherosa Ziehau 			 * netvsc_channel_rollup().
335815516c77SSepherosa Ziehau 			 */
335915516c77SSepherosa Ziehau 			goto again;
336015516c77SSepherosa Ziehau 		}
336115516c77SSepherosa Ziehau 		if_printf(ifp, "send failed\n");
336215516c77SSepherosa Ziehau 
336315516c77SSepherosa Ziehau 		/*
336415516c77SSepherosa Ziehau 		 * Caller will perform further processing on the
336515516c77SSepherosa Ziehau 		 * associated mbuf, so don't free it in hn_txdesc_put();
336615516c77SSepherosa Ziehau 		 * only unload it from the DMA map in hn_txdesc_put(),
336715516c77SSepherosa Ziehau 		 * if it was loaded.
336815516c77SSepherosa Ziehau 		 */
336915516c77SSepherosa Ziehau 		txd->m = NULL;
337015516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
337115516c77SSepherosa Ziehau 		KASSERT(freed != 0,
337215516c77SSepherosa Ziehau 		    ("fail to free txd upon send error"));
337315516c77SSepherosa Ziehau 
337415516c77SSepherosa Ziehau 		txr->hn_send_failed++;
337515516c77SSepherosa Ziehau 	}
3376dc13fee6SSepherosa Ziehau 
3377dc13fee6SSepherosa Ziehau 	/* Reset temporary stats, after this sending is done. */
3378dc13fee6SSepherosa Ziehau 	txr->hn_stat_size = 0;
3379dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts = 0;
3380dc13fee6SSepherosa Ziehau 	txr->hn_stat_mcasts = 0;
3381dc13fee6SSepherosa Ziehau 
3382dc13fee6SSepherosa Ziehau 	return (error);
338315516c77SSepherosa Ziehau }
338415516c77SSepherosa Ziehau 
338515516c77SSepherosa Ziehau /*
338615516c77SSepherosa Ziehau  * Append the specified data to the indicated mbuf chain,
338715516c77SSepherosa Ziehau  * Extend the mbuf chain if the new data does not fit in
338815516c77SSepherosa Ziehau  * existing space.
338915516c77SSepherosa Ziehau  *
339015516c77SSepherosa Ziehau  * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
339115516c77SSepherosa Ziehau  * There should be an equivalent in the kernel mbuf code,
339215516c77SSepherosa Ziehau  * but there does not appear to be one yet.
339315516c77SSepherosa Ziehau  *
339415516c77SSepherosa Ziehau  * Differs from m_append() in that additional mbufs are
339515516c77SSepherosa Ziehau  * allocated with cluster size MJUMPAGESIZE, and filled
339615516c77SSepherosa Ziehau  * accordingly.
339715516c77SSepherosa Ziehau  *
3398a491581fSWei Hu  * Return the last mbuf in the chain or NULL if failed to
3399a491581fSWei Hu  * allocate new mbuf.
340015516c77SSepherosa Ziehau  */
3401a491581fSWei Hu static struct mbuf *
340215516c77SSepherosa Ziehau hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
340315516c77SSepherosa Ziehau {
340415516c77SSepherosa Ziehau 	struct mbuf *m, *n;
340515516c77SSepherosa Ziehau 	int remainder, space;
340615516c77SSepherosa Ziehau 
340715516c77SSepherosa Ziehau 	for (m = m0; m->m_next != NULL; m = m->m_next)
340815516c77SSepherosa Ziehau 		;
340915516c77SSepherosa Ziehau 	remainder = len;
341015516c77SSepherosa Ziehau 	space = M_TRAILINGSPACE(m);
341115516c77SSepherosa Ziehau 	if (space > 0) {
341215516c77SSepherosa Ziehau 		/*
341315516c77SSepherosa Ziehau 		 * Copy into available space.
341415516c77SSepherosa Ziehau 		 */
341515516c77SSepherosa Ziehau 		if (space > remainder)
341615516c77SSepherosa Ziehau 			space = remainder;
341715516c77SSepherosa Ziehau 		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
341815516c77SSepherosa Ziehau 		m->m_len += space;
341915516c77SSepherosa Ziehau 		cp += space;
342015516c77SSepherosa Ziehau 		remainder -= space;
342115516c77SSepherosa Ziehau 	}
342215516c77SSepherosa Ziehau 	while (remainder > 0) {
342315516c77SSepherosa Ziehau 		/*
342415516c77SSepherosa Ziehau 		 * Allocate a new mbuf; could check space
342515516c77SSepherosa Ziehau 		 * and allocate a cluster instead.
342615516c77SSepherosa Ziehau 		 */
342715516c77SSepherosa Ziehau 		n = m_getjcl(M_NOWAIT, m->m_type, 0, MJUMPAGESIZE);
342815516c77SSepherosa Ziehau 		if (n == NULL)
3429a491581fSWei Hu 			return NULL;
343015516c77SSepherosa Ziehau 		n->m_len = min(MJUMPAGESIZE, remainder);
343115516c77SSepherosa Ziehau 		bcopy(cp, mtod(n, caddr_t), n->m_len);
343215516c77SSepherosa Ziehau 		cp += n->m_len;
343315516c77SSepherosa Ziehau 		remainder -= n->m_len;
343415516c77SSepherosa Ziehau 		m->m_next = n;
343515516c77SSepherosa Ziehau 		m = n;
343615516c77SSepherosa Ziehau 	}
343715516c77SSepherosa Ziehau 
3438a491581fSWei Hu 	return m;
343915516c77SSepherosa Ziehau }
344015516c77SSepherosa Ziehau 
344115516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
344215516c77SSepherosa Ziehau static __inline int
344315516c77SSepherosa Ziehau hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m)
344415516c77SSepherosa Ziehau {
344515516c77SSepherosa Ziehau 	if (hn_lro_mbufq_depth) {
344615516c77SSepherosa Ziehau 		tcp_lro_queue_mbuf(lc, m);
344715516c77SSepherosa Ziehau 		return 0;
344815516c77SSepherosa Ziehau 	}
344915516c77SSepherosa Ziehau 	return tcp_lro_rx(lc, m, 0);
345015516c77SSepherosa Ziehau }
345115516c77SSepherosa Ziehau #endif
345215516c77SSepherosa Ziehau 
345315516c77SSepherosa Ziehau static int
3454a491581fSWei Hu hn_rxpkt(struct hn_rx_ring *rxr)
345515516c77SSepherosa Ziehau {
3456*4db5958aSJustin Hibbits 	if_t ifp, hn_ifp = rxr->hn_ifp;
3457a491581fSWei Hu 	struct mbuf *m_new, *n;
3458642ec226SSepherosa Ziehau 	int size, do_lro = 0, do_csum = 1, is_vf = 0;
3459642ec226SSepherosa Ziehau 	int hash_type = M_HASHTYPE_NONE;
3460db76829bSSepherosa Ziehau 	int l3proto = ETHERTYPE_MAX, l4proto = IPPROTO_DONE;
3461a491581fSWei Hu 	int i;
346215516c77SSepherosa Ziehau 
3463642ec226SSepherosa Ziehau 	ifp = hn_ifp;
3464642ec226SSepherosa Ziehau 	if (rxr->hn_rxvf_ifp != NULL) {
3465a97fff19SSepherosa Ziehau 		/*
3466642ec226SSepherosa Ziehau 		 * Non-transparent mode VF; pretend this packet is from
3467642ec226SSepherosa Ziehau 		 * the VF.
3468a97fff19SSepherosa Ziehau 		 */
3469642ec226SSepherosa Ziehau 		ifp = rxr->hn_rxvf_ifp;
3470642ec226SSepherosa Ziehau 		is_vf = 1;
3471642ec226SSepherosa Ziehau 	} else if (rxr->hn_rx_flags & HN_RX_FLAG_XPNT_VF) {
3472642ec226SSepherosa Ziehau 		/* Transparent mode VF. */
3473642ec226SSepherosa Ziehau 		is_vf = 1;
3474642ec226SSepherosa Ziehau 	}
34755bdfd3fdSDexuan Cui 
3476*4db5958aSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
3477b3b75d9cSSepherosa Ziehau 		/*
3478b3b75d9cSSepherosa Ziehau 		 * NOTE:
3479b3b75d9cSSepherosa Ziehau 		 * See the NOTE of hn_rndis_init_fixat().  This
3480b3b75d9cSSepherosa Ziehau 		 * function can be reached, immediately after the
3481b3b75d9cSSepherosa Ziehau 		 * RNDIS is initialized but before the ifnet is
3482b3b75d9cSSepherosa Ziehau 		 * setup on the hn_attach() path; drop the unexpected
3483b3b75d9cSSepherosa Ziehau 		 * packets.
3484b3b75d9cSSepherosa Ziehau 		 */
3485b3b75d9cSSepherosa Ziehau 		return (0);
3486b3b75d9cSSepherosa Ziehau 	}
3487b3b75d9cSSepherosa Ziehau 
3488a491581fSWei Hu 	if (__predict_false(rxr->rsc.pktlen < ETHER_HDR_LEN)) {
3489a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IERRORS, 1);
3490a97fff19SSepherosa Ziehau 		return (0);
3491a97fff19SSepherosa Ziehau 	}
3492a97fff19SSepherosa Ziehau 
3493a491581fSWei Hu 	if (rxr->rsc.cnt == 1 && rxr->rsc.pktlen <= MHLEN) {
349415516c77SSepherosa Ziehau 		m_new = m_gethdr(M_NOWAIT, MT_DATA);
349515516c77SSepherosa Ziehau 		if (m_new == NULL) {
3496a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
349715516c77SSepherosa Ziehau 			return (0);
349815516c77SSepherosa Ziehau 		}
3499a491581fSWei Hu 		memcpy(mtod(m_new, void *), rxr->rsc.frag_data[0],
3500a491581fSWei Hu 		    rxr->rsc.frag_len[0]);
3501a491581fSWei Hu 		m_new->m_pkthdr.len = m_new->m_len = rxr->rsc.frag_len[0];
350215516c77SSepherosa Ziehau 	} else {
350315516c77SSepherosa Ziehau 		/*
350415516c77SSepherosa Ziehau 		 * Get an mbuf with a cluster.  For packets 2K or less,
350515516c77SSepherosa Ziehau 		 * get a standard 2K cluster.  For anything larger, get a
350615516c77SSepherosa Ziehau 		 * 4K cluster.  Any buffers larger than 4K can cause problems
350715516c77SSepherosa Ziehau 		 * if looped around to the Hyper-V TX channel, so avoid them.
350815516c77SSepherosa Ziehau 		 */
350915516c77SSepherosa Ziehau 		size = MCLBYTES;
3510a491581fSWei Hu 		if (rxr->rsc.pktlen > MCLBYTES) {
351115516c77SSepherosa Ziehau 			/* 4096 */
351215516c77SSepherosa Ziehau 			size = MJUMPAGESIZE;
351315516c77SSepherosa Ziehau 		}
351415516c77SSepherosa Ziehau 
351515516c77SSepherosa Ziehau 		m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size);
351615516c77SSepherosa Ziehau 		if (m_new == NULL) {
3517a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
351815516c77SSepherosa Ziehau 			return (0);
351915516c77SSepherosa Ziehau 		}
352015516c77SSepherosa Ziehau 
3521a491581fSWei Hu 		n = m_new;
3522a491581fSWei Hu 		for (i = 0; i < rxr->rsc.cnt; i++) {
3523a491581fSWei Hu 			n = hv_m_append(n, rxr->rsc.frag_len[i],
3524a491581fSWei Hu 			    rxr->rsc.frag_data[i]);
3525a491581fSWei Hu 			if (n == NULL) {
3526a491581fSWei Hu 				if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
3527a491581fSWei Hu 				return (0);
3528a491581fSWei Hu 			} else {
3529a491581fSWei Hu 				m_new->m_pkthdr.len += rxr->rsc.frag_len[i];
353015516c77SSepherosa Ziehau 			}
3531a491581fSWei Hu 		}
3532a491581fSWei Hu 	}
3533a491581fSWei Hu 	if (rxr->rsc.pktlen <= MHLEN)
3534a491581fSWei Hu 		rxr->hn_small_pkts++;
3535a491581fSWei Hu 
353615516c77SSepherosa Ziehau 	m_new->m_pkthdr.rcvif = ifp;
353715516c77SSepherosa Ziehau 
3538*4db5958aSJustin Hibbits 	if (__predict_false((if_getcapenable(hn_ifp) & IFCAP_RXCSUM) == 0))
353915516c77SSepherosa Ziehau 		do_csum = 0;
354015516c77SSepherosa Ziehau 
354115516c77SSepherosa Ziehau 	/* receive side checksum offload */
3542a491581fSWei Hu 	if (rxr->rsc.csum_info != NULL) {
354315516c77SSepherosa Ziehau 		/* IP csum offload */
3544a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) & NDIS_RXCSUM_INFO_IPCS_OK) && do_csum) {
354515516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
354615516c77SSepherosa Ziehau 			    (CSUM_IP_CHECKED | CSUM_IP_VALID);
354715516c77SSepherosa Ziehau 			rxr->hn_csum_ip++;
354815516c77SSepherosa Ziehau 		}
354915516c77SSepherosa Ziehau 
355015516c77SSepherosa Ziehau 		/* TCP/UDP csum offload */
3551a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) & (NDIS_RXCSUM_INFO_UDPCS_OK |
355215516c77SSepherosa Ziehau 		     NDIS_RXCSUM_INFO_TCPCS_OK)) && do_csum) {
355315516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
355415516c77SSepherosa Ziehau 			    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
355515516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_data = 0xffff;
3556a491581fSWei Hu 			if (*(rxr->rsc.csum_info) & NDIS_RXCSUM_INFO_TCPCS_OK)
355715516c77SSepherosa Ziehau 				rxr->hn_csum_tcp++;
355815516c77SSepherosa Ziehau 			else
355915516c77SSepherosa Ziehau 				rxr->hn_csum_udp++;
356015516c77SSepherosa Ziehau 		}
356115516c77SSepherosa Ziehau 
356215516c77SSepherosa Ziehau 		/*
356315516c77SSepherosa Ziehau 		 * XXX
356415516c77SSepherosa Ziehau 		 * As of this write (Oct 28th, 2016), host side will turn
356515516c77SSepherosa Ziehau 		 * on only TCPCS_OK and IPCS_OK even for UDP datagrams, so
356615516c77SSepherosa Ziehau 		 * the do_lro setting here is actually _not_ accurate.  We
356715516c77SSepherosa Ziehau 		 * depend on the RSS hash type check to reset do_lro.
356815516c77SSepherosa Ziehau 		 */
3569a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) &
357015516c77SSepherosa Ziehau 		     (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) ==
357115516c77SSepherosa Ziehau 		    (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK))
357215516c77SSepherosa Ziehau 			do_lro = 1;
357315516c77SSepherosa Ziehau 	} else {
3574db76829bSSepherosa Ziehau 		hn_rxpkt_proto(m_new, &l3proto, &l4proto);
3575db76829bSSepherosa Ziehau 		if (l3proto == ETHERTYPE_IP) {
3576db76829bSSepherosa Ziehau 			if (l4proto == IPPROTO_TCP) {
357715516c77SSepherosa Ziehau 				if (do_csum &&
357815516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
357915516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_TCP)) {
358015516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
358115516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
358215516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
358315516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
358415516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
358515516c77SSepherosa Ziehau 				}
358615516c77SSepherosa Ziehau 				do_lro = 1;
3587db76829bSSepherosa Ziehau 			} else if (l4proto == IPPROTO_UDP) {
358815516c77SSepherosa Ziehau 				if (do_csum &&
358915516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
359015516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_UDP)) {
359115516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
359215516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
359315516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
359415516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
359515516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
359615516c77SSepherosa Ziehau 				}
3597db76829bSSepherosa Ziehau 			} else if (l4proto != IPPROTO_DONE && do_csum &&
359815516c77SSepherosa Ziehau 			    (rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) {
359915516c77SSepherosa Ziehau 				rxr->hn_csum_trusted++;
360015516c77SSepherosa Ziehau 				m_new->m_pkthdr.csum_flags |=
360115516c77SSepherosa Ziehau 				    (CSUM_IP_CHECKED | CSUM_IP_VALID);
360215516c77SSepherosa Ziehau 			}
360315516c77SSepherosa Ziehau 		}
360415516c77SSepherosa Ziehau 	}
3605db76829bSSepherosa Ziehau 
3606a491581fSWei Hu 	if (rxr->rsc.vlan_info != NULL) {
360715516c77SSepherosa Ziehau 		m_new->m_pkthdr.ether_vtag = EVL_MAKETAG(
3608a491581fSWei Hu 		    NDIS_VLAN_INFO_ID(*(rxr->rsc.vlan_info)),
3609a491581fSWei Hu 		    NDIS_VLAN_INFO_PRI(*(rxr->rsc.vlan_info)),
3610a491581fSWei Hu 		    NDIS_VLAN_INFO_CFI(*(rxr->rsc.vlan_info)));
361115516c77SSepherosa Ziehau 		m_new->m_flags |= M_VLANTAG;
361215516c77SSepherosa Ziehau 	}
361315516c77SSepherosa Ziehau 
3614a97fff19SSepherosa Ziehau 	/*
3615a97fff19SSepherosa Ziehau 	 * If VF is activated (tranparent/non-transparent mode does not
3616a97fff19SSepherosa Ziehau 	 * matter here).
3617a97fff19SSepherosa Ziehau 	 *
3618a97fff19SSepherosa Ziehau 	 * - Disable LRO
3619a97fff19SSepherosa Ziehau 	 *
3620a97fff19SSepherosa Ziehau 	 *   hn(4) will only receive broadcast packets, multicast packets,
3621a97fff19SSepherosa Ziehau 	 *   TCP SYN and SYN|ACK (in Azure), LRO is useless for these
3622a97fff19SSepherosa Ziehau 	 *   packet types.
3623a97fff19SSepherosa Ziehau 	 *
3624a97fff19SSepherosa Ziehau 	 *   For non-transparent, we definitely _cannot_ enable LRO at
3625a97fff19SSepherosa Ziehau 	 *   all, since the LRO flush will use hn(4) as the receiving
3626a97fff19SSepherosa Ziehau 	 *   interface; i.e. hn_ifp->if_input(hn_ifp, m).
3627a97fff19SSepherosa Ziehau 	 */
3628642ec226SSepherosa Ziehau 	if (is_vf)
3629642ec226SSepherosa Ziehau 		do_lro = 0;
3630a97fff19SSepherosa Ziehau 
3631642ec226SSepherosa Ziehau 	/*
3632642ec226SSepherosa Ziehau 	 * If VF is activated (tranparent/non-transparent mode does not
3633642ec226SSepherosa Ziehau 	 * matter here), do _not_ mess with unsupported hash types or
3634642ec226SSepherosa Ziehau 	 * functions.
3635642ec226SSepherosa Ziehau 	 */
3636a491581fSWei Hu 	if (rxr->rsc.hash_info != NULL) {
363715516c77SSepherosa Ziehau 		rxr->hn_rss_pkts++;
3638a491581fSWei Hu 		m_new->m_pkthdr.flowid = *(rxr->rsc.hash_value);
3639642ec226SSepherosa Ziehau 		if (!is_vf)
364015516c77SSepherosa Ziehau 			hash_type = M_HASHTYPE_OPAQUE_HASH;
3641a491581fSWei Hu 		if ((*(rxr->rsc.hash_info) & NDIS_HASH_FUNCTION_MASK) ==
364215516c77SSepherosa Ziehau 		    NDIS_HASH_FUNCTION_TOEPLITZ) {
3643a491581fSWei Hu 			uint32_t type = (*(rxr->rsc.hash_info) & NDIS_HASH_TYPE_MASK &
3644642ec226SSepherosa Ziehau 			    rxr->hn_mbuf_hash);
364515516c77SSepherosa Ziehau 
364615516c77SSepherosa Ziehau 			/*
364715516c77SSepherosa Ziehau 			 * NOTE:
364815516c77SSepherosa Ziehau 			 * do_lro is resetted, if the hash types are not TCP
364915516c77SSepherosa Ziehau 			 * related.  See the comment in the above csum_flags
365015516c77SSepherosa Ziehau 			 * setup section.
365115516c77SSepherosa Ziehau 			 */
365215516c77SSepherosa Ziehau 			switch (type) {
365315516c77SSepherosa Ziehau 			case NDIS_HASH_IPV4:
365415516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV4;
365515516c77SSepherosa Ziehau 				do_lro = 0;
365615516c77SSepherosa Ziehau 				break;
365715516c77SSepherosa Ziehau 
365815516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV4:
365915516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV4;
3660db76829bSSepherosa Ziehau 				if (rxr->hn_rx_flags & HN_RX_FLAG_UDP_HASH) {
3661db76829bSSepherosa Ziehau 					int def_htype = M_HASHTYPE_OPAQUE_HASH;
3662db76829bSSepherosa Ziehau 
3663db76829bSSepherosa Ziehau 					if (is_vf)
3664db76829bSSepherosa Ziehau 						def_htype = M_HASHTYPE_NONE;
3665db76829bSSepherosa Ziehau 
3666db76829bSSepherosa Ziehau 					/*
3667db76829bSSepherosa Ziehau 					 * UDP 4-tuple hash is delivered as
3668db76829bSSepherosa Ziehau 					 * TCP 4-tuple hash.
3669db76829bSSepherosa Ziehau 					 */
3670db76829bSSepherosa Ziehau 					if (l3proto == ETHERTYPE_MAX) {
3671db76829bSSepherosa Ziehau 						hn_rxpkt_proto(m_new,
3672db76829bSSepherosa Ziehau 						    &l3proto, &l4proto);
3673db76829bSSepherosa Ziehau 					}
3674db76829bSSepherosa Ziehau 					if (l3proto == ETHERTYPE_IP) {
36756f12c42eSSepherosa Ziehau 						if (l4proto == IPPROTO_UDP &&
36766f12c42eSSepherosa Ziehau 						    (rxr->hn_mbuf_hash &
36776f12c42eSSepherosa Ziehau 						     NDIS_HASH_UDP_IPV4_X)) {
3678db76829bSSepherosa Ziehau 							hash_type =
3679db76829bSSepherosa Ziehau 							M_HASHTYPE_RSS_UDP_IPV4;
3680db76829bSSepherosa Ziehau 							do_lro = 0;
3681db76829bSSepherosa Ziehau 						} else if (l4proto !=
3682db76829bSSepherosa Ziehau 						    IPPROTO_TCP) {
3683db76829bSSepherosa Ziehau 							hash_type = def_htype;
3684db76829bSSepherosa Ziehau 							do_lro = 0;
3685db76829bSSepherosa Ziehau 						}
3686db76829bSSepherosa Ziehau 					} else {
3687db76829bSSepherosa Ziehau 						hash_type = def_htype;
3688db76829bSSepherosa Ziehau 						do_lro = 0;
3689db76829bSSepherosa Ziehau 					}
3690db76829bSSepherosa Ziehau 				}
369115516c77SSepherosa Ziehau 				break;
369215516c77SSepherosa Ziehau 
369315516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6:
369415516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6;
369515516c77SSepherosa Ziehau 				do_lro = 0;
369615516c77SSepherosa Ziehau 				break;
369715516c77SSepherosa Ziehau 
369815516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6_EX:
369915516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6_EX;
370015516c77SSepherosa Ziehau 				do_lro = 0;
370115516c77SSepherosa Ziehau 				break;
370215516c77SSepherosa Ziehau 
370315516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6:
370415516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6;
370515516c77SSepherosa Ziehau 				break;
370615516c77SSepherosa Ziehau 
370715516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6_EX:
370815516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6_EX;
370915516c77SSepherosa Ziehau 				break;
371015516c77SSepherosa Ziehau 			}
371115516c77SSepherosa Ziehau 		}
3712642ec226SSepherosa Ziehau 	} else if (!is_vf) {
371315516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = rxr->hn_rx_idx;
371415516c77SSepherosa Ziehau 		hash_type = M_HASHTYPE_OPAQUE;
371515516c77SSepherosa Ziehau 	}
371615516c77SSepherosa Ziehau 	M_HASHTYPE_SET(m_new, hash_type);
371715516c77SSepherosa Ziehau 
3718a97fff19SSepherosa Ziehau 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
3719a97fff19SSepherosa Ziehau 	if (hn_ifp != ifp) {
3720a97fff19SSepherosa Ziehau 		const struct ether_header *eh;
3721a97fff19SSepherosa Ziehau 
372215516c77SSepherosa Ziehau 		/*
3723a97fff19SSepherosa Ziehau 		 * Non-transparent mode VF is activated.
372415516c77SSepherosa Ziehau 		 */
372515516c77SSepherosa Ziehau 
3726a97fff19SSepherosa Ziehau 		/*
3727a97fff19SSepherosa Ziehau 		 * Allow tapping on hn(4).
3728a97fff19SSepherosa Ziehau 		 */
3729a97fff19SSepherosa Ziehau 		ETHER_BPF_MTAP(hn_ifp, m_new);
3730a97fff19SSepherosa Ziehau 
3731a97fff19SSepherosa Ziehau 		/*
3732a97fff19SSepherosa Ziehau 		 * Update hn(4)'s stats.
3733a97fff19SSepherosa Ziehau 		 */
3734a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
3735a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IBYTES, m_new->m_pkthdr.len);
3736a97fff19SSepherosa Ziehau 		/* Checked at the beginning of this function. */
3737a97fff19SSepherosa Ziehau 		KASSERT(m_new->m_len >= ETHER_HDR_LEN, ("not ethernet frame"));
3738a97fff19SSepherosa Ziehau 		eh = mtod(m_new, struct ether_header *);
3739a97fff19SSepherosa Ziehau 		if (ETHER_IS_MULTICAST(eh->ether_dhost))
3740a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IMCASTS, 1);
3741a97fff19SSepherosa Ziehau 	}
374215516c77SSepherosa Ziehau 	rxr->hn_pkts++;
374315516c77SSepherosa Ziehau 
3744*4db5958aSJustin Hibbits 	if ((if_getcapenable(hn_ifp) & IFCAP_LRO) && do_lro) {
374515516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
374615516c77SSepherosa Ziehau 		struct lro_ctrl *lro = &rxr->hn_lro;
374715516c77SSepherosa Ziehau 
374815516c77SSepherosa Ziehau 		if (lro->lro_cnt) {
374915516c77SSepherosa Ziehau 			rxr->hn_lro_tried++;
375015516c77SSepherosa Ziehau 			if (hn_lro_rx(lro, m_new) == 0) {
375115516c77SSepherosa Ziehau 				/* DONE! */
375215516c77SSepherosa Ziehau 				return 0;
375315516c77SSepherosa Ziehau 			}
375415516c77SSepherosa Ziehau 		}
375515516c77SSepherosa Ziehau #endif
375615516c77SSepherosa Ziehau 	}
3757*4db5958aSJustin Hibbits 	if_input(ifp, m_new);
375815516c77SSepherosa Ziehau 
375915516c77SSepherosa Ziehau 	return (0);
376015516c77SSepherosa Ziehau }
376115516c77SSepherosa Ziehau 
376215516c77SSepherosa Ziehau static int
3763*4db5958aSJustin Hibbits hn_ioctl(if_t ifp, u_long cmd, caddr_t data)
376415516c77SSepherosa Ziehau {
3765*4db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
37669c6cae24SSepherosa Ziehau 	struct ifreq *ifr = (struct ifreq *)data, ifr_vf;
3767*4db5958aSJustin Hibbits 	if_t vf_ifp;
376815516c77SSepherosa Ziehau 	int mask, error = 0;
37698c068aa5SSepherosa Ziehau 	struct ifrsskey *ifrk;
37708c068aa5SSepherosa Ziehau 	struct ifrsshash *ifrh;
3771eb2fe044SSepherosa Ziehau 	uint32_t mtu;
377215516c77SSepherosa Ziehau 
377315516c77SSepherosa Ziehau 	switch (cmd) {
377415516c77SSepherosa Ziehau 	case SIOCSIFMTU:
377515516c77SSepherosa Ziehau 		if (ifr->ifr_mtu > HN_MTU_MAX) {
377615516c77SSepherosa Ziehau 			error = EINVAL;
377715516c77SSepherosa Ziehau 			break;
377815516c77SSepherosa Ziehau 		}
377915516c77SSepherosa Ziehau 
378015516c77SSepherosa Ziehau 		HN_LOCK(sc);
378115516c77SSepherosa Ziehau 
378215516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
378315516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
378415516c77SSepherosa Ziehau 			break;
378515516c77SSepherosa Ziehau 		}
378615516c77SSepherosa Ziehau 
378715516c77SSepherosa Ziehau 		if ((sc->hn_caps & HN_CAP_MTU) == 0) {
378815516c77SSepherosa Ziehau 			/* Can't change MTU */
378915516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
379015516c77SSepherosa Ziehau 			error = EOPNOTSUPP;
379115516c77SSepherosa Ziehau 			break;
379215516c77SSepherosa Ziehau 		}
379315516c77SSepherosa Ziehau 
3794*4db5958aSJustin Hibbits 		if (if_getmtu(ifp) == ifr->ifr_mtu) {
379515516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
379615516c77SSepherosa Ziehau 			break;
379715516c77SSepherosa Ziehau 		}
379815516c77SSepherosa Ziehau 
37999c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
38009c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
38019c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
3802*4db5958aSJustin Hibbits 			strlcpy(ifr_vf.ifr_name, if_name(vf_ifp),
38039c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
3804*4db5958aSJustin Hibbits 			error = ifhwioctl(SIOCSIFMTU,vf_ifp,
3805*4db5958aSJustin Hibbits 			    (caddr_t)&ifr_vf, curthread);
38069c6cae24SSepherosa Ziehau 			if (error) {
38079c6cae24SSepherosa Ziehau 				HN_UNLOCK(sc);
38089c6cae24SSepherosa Ziehau 				if_printf(ifp, "%s SIOCSIFMTU %d failed: %d\n",
3809*4db5958aSJustin Hibbits 				    if_name(vf_ifp), ifr->ifr_mtu, error);
38109c6cae24SSepherosa Ziehau 				break;
38119c6cae24SSepherosa Ziehau 			}
38129c6cae24SSepherosa Ziehau 		}
38139c6cae24SSepherosa Ziehau 
381415516c77SSepherosa Ziehau 		/*
381515516c77SSepherosa Ziehau 		 * Suspend this interface before the synthetic parts
381615516c77SSepherosa Ziehau 		 * are ripped.
381715516c77SSepherosa Ziehau 		 */
381815516c77SSepherosa Ziehau 		hn_suspend(sc);
381915516c77SSepherosa Ziehau 
382015516c77SSepherosa Ziehau 		/*
382115516c77SSepherosa Ziehau 		 * Detach the synthetics parts, i.e. NVS and RNDIS.
382215516c77SSepherosa Ziehau 		 */
382315516c77SSepherosa Ziehau 		hn_synth_detach(sc);
382415516c77SSepherosa Ziehau 
382515516c77SSepherosa Ziehau 		/*
382615516c77SSepherosa Ziehau 		 * Reattach the synthetic parts, i.e. NVS and RNDIS,
382715516c77SSepherosa Ziehau 		 * with the new MTU setting.
382815516c77SSepherosa Ziehau 		 */
382915516c77SSepherosa Ziehau 		error = hn_synth_attach(sc, ifr->ifr_mtu);
383015516c77SSepherosa Ziehau 		if (error) {
383115516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
383215516c77SSepherosa Ziehau 			break;
383315516c77SSepherosa Ziehau 		}
383415516c77SSepherosa Ziehau 
3835eb2fe044SSepherosa Ziehau 		error = hn_rndis_get_mtu(sc, &mtu);
3836eb2fe044SSepherosa Ziehau 		if (error)
3837eb2fe044SSepherosa Ziehau 			mtu = ifr->ifr_mtu;
3838eb2fe044SSepherosa Ziehau 		else if (bootverbose)
3839eb2fe044SSepherosa Ziehau 			if_printf(ifp, "RNDIS mtu %u\n", mtu);
3840eb2fe044SSepherosa Ziehau 
384115516c77SSepherosa Ziehau 		/*
384215516c77SSepherosa Ziehau 		 * Commit the requested MTU, after the synthetic parts
384315516c77SSepherosa Ziehau 		 * have been successfully attached.
384415516c77SSepherosa Ziehau 		 */
3845eb2fe044SSepherosa Ziehau 		if (mtu >= ifr->ifr_mtu) {
3846eb2fe044SSepherosa Ziehau 			mtu = ifr->ifr_mtu;
3847eb2fe044SSepherosa Ziehau 		} else {
3848eb2fe044SSepherosa Ziehau 			if_printf(ifp, "fixup mtu %d -> %u\n",
3849eb2fe044SSepherosa Ziehau 			    ifr->ifr_mtu, mtu);
3850eb2fe044SSepherosa Ziehau 		}
3851*4db5958aSJustin Hibbits 		if_setmtu(ifp, mtu);
385215516c77SSepherosa Ziehau 
385315516c77SSepherosa Ziehau 		/*
38549c6cae24SSepherosa Ziehau 		 * Synthetic parts' reattach may change the chimney
38559c6cae24SSepherosa Ziehau 		 * sending size; update it.
385615516c77SSepherosa Ziehau 		 */
385715516c77SSepherosa Ziehau 		if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
385815516c77SSepherosa Ziehau 			hn_set_chim_size(sc, sc->hn_chim_szmax);
38599c6cae24SSepherosa Ziehau 
38609c6cae24SSepherosa Ziehau 		/*
38619c6cae24SSepherosa Ziehau 		 * Make sure that various parameters based on MTU are
38629c6cae24SSepherosa Ziehau 		 * still valid, after the MTU change.
38639c6cae24SSepherosa Ziehau 		 */
38649c6cae24SSepherosa Ziehau 		hn_mtu_change_fixup(sc);
386515516c77SSepherosa Ziehau 
386615516c77SSepherosa Ziehau 		/*
386715516c77SSepherosa Ziehau 		 * All done!  Resume the interface now.
386815516c77SSepherosa Ziehau 		 */
386915516c77SSepherosa Ziehau 		hn_resume(sc);
387015516c77SSepherosa Ziehau 
3871d0cd8231SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXVF) ||
3872d0cd8231SSepherosa Ziehau 		    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
38739c6cae24SSepherosa Ziehau 			/*
38749c6cae24SSepherosa Ziehau 			 * Since we have reattached the NVS part,
38759c6cae24SSepherosa Ziehau 			 * change the datapath to VF again; in case
38769c6cae24SSepherosa Ziehau 			 * that it is lost, after the NVS was detached.
38779c6cae24SSepherosa Ziehau 			 */
38789c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
38799c6cae24SSepherosa Ziehau 		}
38809c6cae24SSepherosa Ziehau 
388115516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
388215516c77SSepherosa Ziehau 		break;
388315516c77SSepherosa Ziehau 
388415516c77SSepherosa Ziehau 	case SIOCSIFFLAGS:
388515516c77SSepherosa Ziehau 		HN_LOCK(sc);
388615516c77SSepherosa Ziehau 
388715516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
388815516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
388915516c77SSepherosa Ziehau 			break;
389015516c77SSepherosa Ziehau 		}
389115516c77SSepherosa Ziehau 
38929c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc))
38939c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
38949c6cae24SSepherosa Ziehau 
3895*4db5958aSJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
3896*4db5958aSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
3897fdc4f478SSepherosa Ziehau 				/*
3898fdc4f478SSepherosa Ziehau 				 * Caller meight hold mutex, e.g.
3899fdc4f478SSepherosa Ziehau 				 * bpf; use busy-wait for the RNDIS
3900fdc4f478SSepherosa Ziehau 				 * reply.
3901fdc4f478SSepherosa Ziehau 				 */
3902fdc4f478SSepherosa Ziehau 				HN_NO_SLEEPING(sc);
3903c08f7b2cSSepherosa Ziehau 				hn_rxfilter_config(sc);
3904fdc4f478SSepherosa Ziehau 				HN_SLEEPING_OK(sc);
39059c6cae24SSepherosa Ziehau 
39069c6cae24SSepherosa Ziehau 				if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
39079c6cae24SSepherosa Ziehau 					error = hn_xpnt_vf_iocsetflags(sc);
3908fdc4f478SSepherosa Ziehau 			} else {
390915516c77SSepherosa Ziehau 				hn_init_locked(sc);
3910fdc4f478SSepherosa Ziehau 			}
391115516c77SSepherosa Ziehau 		} else {
3912*4db5958aSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
39135bdfd3fdSDexuan Cui 				hn_stop(sc, false);
391415516c77SSepherosa Ziehau 		}
3915*4db5958aSJustin Hibbits 		sc->hn_if_flags = if_getflags(ifp);
391615516c77SSepherosa Ziehau 
391715516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
391815516c77SSepherosa Ziehau 		break;
391915516c77SSepherosa Ziehau 
392015516c77SSepherosa Ziehau 	case SIOCSIFCAP:
392115516c77SSepherosa Ziehau 		HN_LOCK(sc);
39229c6cae24SSepherosa Ziehau 
39239c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
39249c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
3925*4db5958aSJustin Hibbits 			strlcpy(ifr_vf.ifr_name, if_name(sc->hn_vf_ifp),
39269c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
39279c6cae24SSepherosa Ziehau 			error = hn_xpnt_vf_iocsetcaps(sc, &ifr_vf);
39289c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
39299c6cae24SSepherosa Ziehau 			break;
39309c6cae24SSepherosa Ziehau 		}
39319c6cae24SSepherosa Ziehau 
39329c6cae24SSepherosa Ziehau 		/*
39339c6cae24SSepherosa Ziehau 		 * Fix up requested capabilities w/ supported capabilities,
39349c6cae24SSepherosa Ziehau 		 * since the supported capabilities could have been changed.
39359c6cae24SSepherosa Ziehau 		 */
3936*4db5958aSJustin Hibbits 		mask = (ifr->ifr_reqcap & if_getcapabilities(ifp)) ^
3937*4db5958aSJustin Hibbits 		    if_getcapenable(ifp);
393815516c77SSepherosa Ziehau 
393915516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM) {
3940*4db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TXCSUM);
3941*4db5958aSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TXCSUM)
3942*4db5958aSJustin Hibbits 				if_sethwassistbits(ifp, HN_CSUM_IP_HWASSIST(sc), 0);
394315516c77SSepherosa Ziehau 			else
3944*4db5958aSJustin Hibbits 				if_sethwassistbits(ifp, 0, HN_CSUM_IP_HWASSIST(sc));
394515516c77SSepherosa Ziehau 		}
394615516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM_IPV6) {
3947*4db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TXCSUM_IPV6);
3948*4db5958aSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6)
3949*4db5958aSJustin Hibbits 				if_sethwassistbits(ifp, HN_CSUM_IP6_HWASSIST(sc), 0);
395015516c77SSepherosa Ziehau 			else
3951*4db5958aSJustin Hibbits 				if_sethwassistbits(ifp, 0, HN_CSUM_IP6_HWASSIST(sc));
395215516c77SSepherosa Ziehau 		}
395315516c77SSepherosa Ziehau 
395415516c77SSepherosa Ziehau 		/* TODO: flip RNDIS offload parameters for RXCSUM. */
395515516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
3956*4db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_RXCSUM);
395715516c77SSepherosa Ziehau #ifdef foo
395815516c77SSepherosa Ziehau 		/* We can't diff IPv6 packets from IPv4 packets on RX path. */
395915516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM_IPV6)
3960*4db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_RXCSUM_IPV6);
396115516c77SSepherosa Ziehau #endif
396215516c77SSepherosa Ziehau 
396315516c77SSepherosa Ziehau 		if (mask & IFCAP_LRO)
3964*4db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_LRO);
396515516c77SSepherosa Ziehau 
396615516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO4) {
3967*4db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TSO4);
3968*4db5958aSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TSO4)
3969*4db5958aSJustin Hibbits 				if_sethwassistbits(ifp, CSUM_IP_TSO, 0);
397015516c77SSepherosa Ziehau 			else
3971*4db5958aSJustin Hibbits 				if_sethwassistbits(ifp, 0, CSUM_IP_TSO);
397215516c77SSepherosa Ziehau 		}
397315516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO6) {
3974*4db5958aSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TSO6);
3975*4db5958aSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TSO6)
3976*4db5958aSJustin Hibbits 				if_sethwassistbits(ifp, CSUM_IP6_TSO, 0);
397715516c77SSepherosa Ziehau 			else
3978*4db5958aSJustin Hibbits 				if_sethwassistbits(ifp, 0, CSUM_IP6_TSO);
397915516c77SSepherosa Ziehau 		}
398015516c77SSepherosa Ziehau 
398115516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
398215516c77SSepherosa Ziehau 		break;
398315516c77SSepherosa Ziehau 
398415516c77SSepherosa Ziehau 	case SIOCADDMULTI:
398515516c77SSepherosa Ziehau 	case SIOCDELMULTI:
398615516c77SSepherosa Ziehau 		HN_LOCK(sc);
398715516c77SSepherosa Ziehau 
398815516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
398915516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
399015516c77SSepherosa Ziehau 			break;
399115516c77SSepherosa Ziehau 		}
3992*4db5958aSJustin Hibbits 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
3993fdc4f478SSepherosa Ziehau 			/*
3994fdc4f478SSepherosa Ziehau 			 * Multicast uses mutex; use busy-wait for
3995fdc4f478SSepherosa Ziehau 			 * the RNDIS reply.
3996fdc4f478SSepherosa Ziehau 			 */
3997fdc4f478SSepherosa Ziehau 			HN_NO_SLEEPING(sc);
3998c08f7b2cSSepherosa Ziehau 			hn_rxfilter_config(sc);
3999fdc4f478SSepherosa Ziehau 			HN_SLEEPING_OK(sc);
4000fdc4f478SSepherosa Ziehau 		}
400115516c77SSepherosa Ziehau 
40029c6cae24SSepherosa Ziehau 		/* XXX vlan(4) style mcast addr maintenance */
40039c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
40049c6cae24SSepherosa Ziehau 			int old_if_flags;
40059c6cae24SSepherosa Ziehau 
4006*4db5958aSJustin Hibbits 			old_if_flags = if_getflags(sc->hn_vf_ifp);
40079c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
40089c6cae24SSepherosa Ziehau 
40099c6cae24SSepherosa Ziehau 			if ((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) &&
4010*4db5958aSJustin Hibbits 			    ((old_if_flags ^ if_getflags(sc->hn_vf_ifp)) &
40119c6cae24SSepherosa Ziehau 			     IFF_ALLMULTI))
40129c6cae24SSepherosa Ziehau 				error = hn_xpnt_vf_iocsetflags(sc);
40139c6cae24SSepherosa Ziehau 		}
40149c6cae24SSepherosa Ziehau 
401515516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
401615516c77SSepherosa Ziehau 		break;
401715516c77SSepherosa Ziehau 
401815516c77SSepherosa Ziehau 	case SIOCSIFMEDIA:
401915516c77SSepherosa Ziehau 	case SIOCGIFMEDIA:
40209c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
40219c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
40229c6cae24SSepherosa Ziehau 			/*
40239c6cae24SSepherosa Ziehau 			 * SIOCGIFMEDIA expects ifmediareq, so don't
40249c6cae24SSepherosa Ziehau 			 * create and pass ifr_vf to the VF here; just
40259c6cae24SSepherosa Ziehau 			 * replace the ifr_name.
40269c6cae24SSepherosa Ziehau 			 */
40279c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
4028*4db5958aSJustin Hibbits 			strlcpy(ifr->ifr_name, if_name(vf_ifp),
40299c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
4030*4db5958aSJustin Hibbits 			error = ifhwioctl(cmd, vf_ifp, data, curthread);
40319c6cae24SSepherosa Ziehau 			/* Restore the ifr_name. */
4032*4db5958aSJustin Hibbits 			strlcpy(ifr->ifr_name, if_name(ifp),
40339c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
40349c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
40359c6cae24SSepherosa Ziehau 			break;
40369c6cae24SSepherosa Ziehau 		}
40379c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
403815516c77SSepherosa Ziehau 		error = ifmedia_ioctl(ifp, ifr, &sc->hn_media, cmd);
403915516c77SSepherosa Ziehau 		break;
404015516c77SSepherosa Ziehau 
40418c068aa5SSepherosa Ziehau 	case SIOCGIFRSSHASH:
40428c068aa5SSepherosa Ziehau 		ifrh = (struct ifrsshash *)data;
40438c068aa5SSepherosa Ziehau 		HN_LOCK(sc);
40448c068aa5SSepherosa Ziehau 		if (sc->hn_rx_ring_inuse == 1) {
40458c068aa5SSepherosa Ziehau 			HN_UNLOCK(sc);
40468c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_NONE;
40478c068aa5SSepherosa Ziehau 			ifrh->ifrh_types = 0;
40488c068aa5SSepherosa Ziehau 			break;
40498c068aa5SSepherosa Ziehau 		}
40508c068aa5SSepherosa Ziehau 
40518c068aa5SSepherosa Ziehau 		if (sc->hn_rss_hash & NDIS_HASH_FUNCTION_TOEPLITZ)
40528c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_TOEPLITZ;
40538c068aa5SSepherosa Ziehau 		else
40548c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_PRIVATE;
4055642ec226SSepherosa Ziehau 		ifrh->ifrh_types = hn_rss_type_fromndis(sc->hn_rss_hash);
40568c068aa5SSepherosa Ziehau 		HN_UNLOCK(sc);
40578c068aa5SSepherosa Ziehau 		break;
40588c068aa5SSepherosa Ziehau 
40598c068aa5SSepherosa Ziehau 	case SIOCGIFRSSKEY:
40608c068aa5SSepherosa Ziehau 		ifrk = (struct ifrsskey *)data;
40618c068aa5SSepherosa Ziehau 		HN_LOCK(sc);
40628c068aa5SSepherosa Ziehau 		if (sc->hn_rx_ring_inuse == 1) {
40638c068aa5SSepherosa Ziehau 			HN_UNLOCK(sc);
40648c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_NONE;
40658c068aa5SSepherosa Ziehau 			ifrk->ifrk_keylen = 0;
40668c068aa5SSepherosa Ziehau 			break;
40678c068aa5SSepherosa Ziehau 		}
40688c068aa5SSepherosa Ziehau 		if (sc->hn_rss_hash & NDIS_HASH_FUNCTION_TOEPLITZ)
40698c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_TOEPLITZ;
40708c068aa5SSepherosa Ziehau 		else
40718c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_PRIVATE;
40728c068aa5SSepherosa Ziehau 		ifrk->ifrk_keylen = NDIS_HASH_KEYSIZE_TOEPLITZ;
40738c068aa5SSepherosa Ziehau 		memcpy(ifrk->ifrk_key, sc->hn_rss.rss_key,
40748c068aa5SSepherosa Ziehau 		    NDIS_HASH_KEYSIZE_TOEPLITZ);
40758c068aa5SSepherosa Ziehau 		HN_UNLOCK(sc);
40768c068aa5SSepherosa Ziehau 		break;
40778c068aa5SSepherosa Ziehau 
407815516c77SSepherosa Ziehau 	default:
407915516c77SSepherosa Ziehau 		error = ether_ioctl(ifp, cmd, data);
408015516c77SSepherosa Ziehau 		break;
408115516c77SSepherosa Ziehau 	}
408215516c77SSepherosa Ziehau 	return (error);
408315516c77SSepherosa Ziehau }
408415516c77SSepherosa Ziehau 
408515516c77SSepherosa Ziehau static void
40865bdfd3fdSDexuan Cui hn_stop(struct hn_softc *sc, bool detaching)
408715516c77SSepherosa Ziehau {
4088*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
408915516c77SSepherosa Ziehau 	int i;
409015516c77SSepherosa Ziehau 
409115516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
409215516c77SSepherosa Ziehau 
409315516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
409415516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
409515516c77SSepherosa Ziehau 
40969c6cae24SSepherosa Ziehau 	/* Clear RUNNING bit ASAP. */
4097*4db5958aSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
40989c6cae24SSepherosa Ziehau 
40996c1204dfSSepherosa Ziehau 	/* Disable polling. */
41006c1204dfSSepherosa Ziehau 	hn_polling(sc, 0);
41016c1204dfSSepherosa Ziehau 
41029c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
41039c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_ifp != NULL,
4104*4db5958aSJustin Hibbits 		    ("%s: VF is not attached", if_name(ifp)));
41059c6cae24SSepherosa Ziehau 
4106a97fff19SSepherosa Ziehau 		/* Mark transparent mode VF as disabled. */
4107a97fff19SSepherosa Ziehau 		hn_xpnt_vf_setdisable(sc, false /* keep hn_vf_ifp */);
41089c6cae24SSepherosa Ziehau 
41099c6cae24SSepherosa Ziehau 		/*
41109c6cae24SSepherosa Ziehau 		 * NOTE:
41119c6cae24SSepherosa Ziehau 		 * Datapath setting must happen _before_ bringing
41129c6cae24SSepherosa Ziehau 		 * the VF down.
41139c6cae24SSepherosa Ziehau 		 */
41149c6cae24SSepherosa Ziehau 		hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
41159c6cae24SSepherosa Ziehau 
41169c6cae24SSepherosa Ziehau 		/*
41179c6cae24SSepherosa Ziehau 		 * Bring the VF down.
41189c6cae24SSepherosa Ziehau 		 */
41199c6cae24SSepherosa Ziehau 		hn_xpnt_vf_saveifflags(sc);
4120*4db5958aSJustin Hibbits 		if_setflagbits(ifp, 0, IFF_UP);
41219c6cae24SSepherosa Ziehau 		hn_xpnt_vf_iocsetflags(sc);
41229c6cae24SSepherosa Ziehau 	}
41239c6cae24SSepherosa Ziehau 
41249c6cae24SSepherosa Ziehau 	/* Suspend data transfers. */
412515516c77SSepherosa Ziehau 	hn_suspend_data(sc);
412615516c77SSepherosa Ziehau 
412715516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
4128*4db5958aSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
412915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
413015516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
41315bdfd3fdSDexuan Cui 
41325bdfd3fdSDexuan Cui 	/*
41339c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is active, make sure
41349c6cae24SSepherosa Ziehau 	 * that the RX filter still allows packet reception.
41355bdfd3fdSDexuan Cui 	 */
4136962f0357SSepherosa Ziehau 	if (!detaching && (sc->hn_flags & HN_FLAG_RXVF))
41375bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
413815516c77SSepherosa Ziehau }
413915516c77SSepherosa Ziehau 
414015516c77SSepherosa Ziehau static void
414115516c77SSepherosa Ziehau hn_init_locked(struct hn_softc *sc)
414215516c77SSepherosa Ziehau {
4143*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
414415516c77SSepherosa Ziehau 	int i;
414515516c77SSepherosa Ziehau 
414615516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
414715516c77SSepherosa Ziehau 
414815516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
414915516c77SSepherosa Ziehau 		return;
415015516c77SSepherosa Ziehau 
4151*4db5958aSJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
415215516c77SSepherosa Ziehau 		return;
415315516c77SSepherosa Ziehau 
415415516c77SSepherosa Ziehau 	/* Configure RX filter */
4155c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
415615516c77SSepherosa Ziehau 
415715516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
4158*4db5958aSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
415915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
416015516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
416115516c77SSepherosa Ziehau 
416215516c77SSepherosa Ziehau 	/* Clear TX 'suspended' bit. */
416315516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_inuse);
416415516c77SSepherosa Ziehau 
41659c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
41669c6cae24SSepherosa Ziehau 		/* Initialize transparent VF. */
41679c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
41689c6cae24SSepherosa Ziehau 	}
41699c6cae24SSepherosa Ziehau 
417015516c77SSepherosa Ziehau 	/* Everything is ready; unleash! */
4171*4db5958aSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
41726c1204dfSSepherosa Ziehau 
41736c1204dfSSepherosa Ziehau 	/* Re-enable polling if requested. */
41746c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz > 0)
41756c1204dfSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
417615516c77SSepherosa Ziehau }
417715516c77SSepherosa Ziehau 
417815516c77SSepherosa Ziehau static void
417915516c77SSepherosa Ziehau hn_init(void *xsc)
418015516c77SSepherosa Ziehau {
418115516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
418215516c77SSepherosa Ziehau 
418315516c77SSepherosa Ziehau 	HN_LOCK(sc);
418415516c77SSepherosa Ziehau 	hn_init_locked(sc);
418515516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
418615516c77SSepherosa Ziehau }
418715516c77SSepherosa Ziehau 
418815516c77SSepherosa Ziehau static int
418915516c77SSepherosa Ziehau hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS)
419015516c77SSepherosa Ziehau {
419115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
419215516c77SSepherosa Ziehau 	unsigned int lenlim;
419315516c77SSepherosa Ziehau 	int error;
419415516c77SSepherosa Ziehau 
419515516c77SSepherosa Ziehau 	lenlim = sc->hn_rx_ring[0].hn_lro.lro_length_lim;
419615516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &lenlim, 0, req);
419715516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
419815516c77SSepherosa Ziehau 		return error;
419915516c77SSepherosa Ziehau 
420015516c77SSepherosa Ziehau 	HN_LOCK(sc);
420115516c77SSepherosa Ziehau 	if (lenlim < HN_LRO_LENLIM_MIN(sc->hn_ifp) ||
420215516c77SSepherosa Ziehau 	    lenlim > TCP_LRO_LENGTH_MAX) {
420315516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
420415516c77SSepherosa Ziehau 		return EINVAL;
420515516c77SSepherosa Ziehau 	}
420615516c77SSepherosa Ziehau 	hn_set_lro_lenlim(sc, lenlim);
420715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
420815516c77SSepherosa Ziehau 
420915516c77SSepherosa Ziehau 	return 0;
421015516c77SSepherosa Ziehau }
421115516c77SSepherosa Ziehau 
421215516c77SSepherosa Ziehau static int
421315516c77SSepherosa Ziehau hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS)
421415516c77SSepherosa Ziehau {
421515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
421615516c77SSepherosa Ziehau 	int ackcnt, error, i;
421715516c77SSepherosa Ziehau 
421815516c77SSepherosa Ziehau 	/*
421915516c77SSepherosa Ziehau 	 * lro_ackcnt_lim is append count limit,
422015516c77SSepherosa Ziehau 	 * +1 to turn it into aggregation limit.
422115516c77SSepherosa Ziehau 	 */
422215516c77SSepherosa Ziehau 	ackcnt = sc->hn_rx_ring[0].hn_lro.lro_ackcnt_lim + 1;
422315516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &ackcnt, 0, req);
422415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
422515516c77SSepherosa Ziehau 		return error;
422615516c77SSepherosa Ziehau 
422715516c77SSepherosa Ziehau 	if (ackcnt < 2 || ackcnt > (TCP_LRO_ACKCNT_MAX + 1))
422815516c77SSepherosa Ziehau 		return EINVAL;
422915516c77SSepherosa Ziehau 
423015516c77SSepherosa Ziehau 	/*
423115516c77SSepherosa Ziehau 	 * Convert aggregation limit back to append
423215516c77SSepherosa Ziehau 	 * count limit.
423315516c77SSepherosa Ziehau 	 */
423415516c77SSepherosa Ziehau 	--ackcnt;
423515516c77SSepherosa Ziehau 	HN_LOCK(sc);
4236a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
423715516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_ackcnt_lim = ackcnt;
423815516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
423915516c77SSepherosa Ziehau 	return 0;
424015516c77SSepherosa Ziehau }
424115516c77SSepherosa Ziehau 
424215516c77SSepherosa Ziehau static int
424315516c77SSepherosa Ziehau hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS)
424415516c77SSepherosa Ziehau {
424515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
424615516c77SSepherosa Ziehau 	int hcsum = arg2;
424715516c77SSepherosa Ziehau 	int on, error, i;
424815516c77SSepherosa Ziehau 
424915516c77SSepherosa Ziehau 	on = 0;
425015516c77SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_trust_hcsum & hcsum)
425115516c77SSepherosa Ziehau 		on = 1;
425215516c77SSepherosa Ziehau 
425315516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &on, 0, req);
425415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
425515516c77SSepherosa Ziehau 		return error;
425615516c77SSepherosa Ziehau 
425715516c77SSepherosa Ziehau 	HN_LOCK(sc);
4258a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
425915516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
426015516c77SSepherosa Ziehau 
426115516c77SSepherosa Ziehau 		if (on)
426215516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= hcsum;
426315516c77SSepherosa Ziehau 		else
426415516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum &= ~hcsum;
426515516c77SSepherosa Ziehau 	}
426615516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
426715516c77SSepherosa Ziehau 	return 0;
426815516c77SSepherosa Ziehau }
426915516c77SSepherosa Ziehau 
427015516c77SSepherosa Ziehau static int
427115516c77SSepherosa Ziehau hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS)
427215516c77SSepherosa Ziehau {
427315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
427415516c77SSepherosa Ziehau 	int chim_size, error;
427515516c77SSepherosa Ziehau 
427615516c77SSepherosa Ziehau 	chim_size = sc->hn_tx_ring[0].hn_chim_size;
427715516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &chim_size, 0, req);
427815516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
427915516c77SSepherosa Ziehau 		return error;
428015516c77SSepherosa Ziehau 
428115516c77SSepherosa Ziehau 	if (chim_size > sc->hn_chim_szmax || chim_size <= 0)
428215516c77SSepherosa Ziehau 		return EINVAL;
428315516c77SSepherosa Ziehau 
428415516c77SSepherosa Ziehau 	HN_LOCK(sc);
428515516c77SSepherosa Ziehau 	hn_set_chim_size(sc, chim_size);
428615516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
428715516c77SSepherosa Ziehau 	return 0;
428815516c77SSepherosa Ziehau }
428915516c77SSepherosa Ziehau 
429015516c77SSepherosa Ziehau static int
429115516c77SSepherosa Ziehau hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS)
429215516c77SSepherosa Ziehau {
429315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
429415516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
429515516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
429615516c77SSepherosa Ziehau 	uint64_t stat;
429715516c77SSepherosa Ziehau 
429815516c77SSepherosa Ziehau 	stat = 0;
4299a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
430015516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
430115516c77SSepherosa Ziehau 		stat += *((uint64_t *)((uint8_t *)rxr + ofs));
430215516c77SSepherosa Ziehau 	}
430315516c77SSepherosa Ziehau 
430415516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
430515516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
430615516c77SSepherosa Ziehau 		return error;
430715516c77SSepherosa Ziehau 
430815516c77SSepherosa Ziehau 	/* Zero out this stat. */
4309a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
431015516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
431115516c77SSepherosa Ziehau 		*((uint64_t *)((uint8_t *)rxr + ofs)) = 0;
431215516c77SSepherosa Ziehau 	}
431315516c77SSepherosa Ziehau 	return 0;
431415516c77SSepherosa Ziehau }
431515516c77SSepherosa Ziehau 
431615516c77SSepherosa Ziehau static int
431715516c77SSepherosa Ziehau hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
431815516c77SSepherosa Ziehau {
431915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
432015516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
432115516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
432215516c77SSepherosa Ziehau 	u_long stat;
432315516c77SSepherosa Ziehau 
432415516c77SSepherosa Ziehau 	stat = 0;
4325a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
432615516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
432715516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)rxr + ofs));
432815516c77SSepherosa Ziehau 	}
432915516c77SSepherosa Ziehau 
433015516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
433115516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
433215516c77SSepherosa Ziehau 		return error;
433315516c77SSepherosa Ziehau 
433415516c77SSepherosa Ziehau 	/* Zero out this stat. */
4335a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
433615516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
433715516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)rxr + ofs)) = 0;
433815516c77SSepherosa Ziehau 	}
433915516c77SSepherosa Ziehau 	return 0;
434015516c77SSepherosa Ziehau }
434115516c77SSepherosa Ziehau 
434215516c77SSepherosa Ziehau static int
434315516c77SSepherosa Ziehau hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
434415516c77SSepherosa Ziehau {
434515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
434615516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
434715516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
434815516c77SSepherosa Ziehau 	u_long stat;
434915516c77SSepherosa Ziehau 
435015516c77SSepherosa Ziehau 	stat = 0;
4351a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
435215516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
435315516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)txr + ofs));
435415516c77SSepherosa Ziehau 	}
435515516c77SSepherosa Ziehau 
435615516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
435715516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
435815516c77SSepherosa Ziehau 		return error;
435915516c77SSepherosa Ziehau 
436015516c77SSepherosa Ziehau 	/* Zero out this stat. */
4361a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
436215516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
436315516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)txr + ofs)) = 0;
436415516c77SSepherosa Ziehau 	}
436515516c77SSepherosa Ziehau 	return 0;
436615516c77SSepherosa Ziehau }
436715516c77SSepherosa Ziehau 
436815516c77SSepherosa Ziehau static int
436915516c77SSepherosa Ziehau hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS)
437015516c77SSepherosa Ziehau {
437115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
437215516c77SSepherosa Ziehau 	int ofs = arg2, i, error, conf;
437315516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
437415516c77SSepherosa Ziehau 
437515516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[0];
437615516c77SSepherosa Ziehau 	conf = *((int *)((uint8_t *)txr + ofs));
437715516c77SSepherosa Ziehau 
437815516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &conf, 0, req);
437915516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
438015516c77SSepherosa Ziehau 		return error;
438115516c77SSepherosa Ziehau 
438215516c77SSepherosa Ziehau 	HN_LOCK(sc);
4383a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
438415516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
438515516c77SSepherosa Ziehau 		*((int *)((uint8_t *)txr + ofs)) = conf;
438615516c77SSepherosa Ziehau 	}
438715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
438815516c77SSepherosa Ziehau 
438915516c77SSepherosa Ziehau 	return 0;
439015516c77SSepherosa Ziehau }
439115516c77SSepherosa Ziehau 
439215516c77SSepherosa Ziehau static int
4393dc13fee6SSepherosa Ziehau hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS)
4394dc13fee6SSepherosa Ziehau {
4395dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4396dc13fee6SSepherosa Ziehau 	int error, size;
4397dc13fee6SSepherosa Ziehau 
4398dc13fee6SSepherosa Ziehau 	size = sc->hn_agg_size;
4399dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &size, 0, req);
4400dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
4401dc13fee6SSepherosa Ziehau 		return (error);
4402dc13fee6SSepherosa Ziehau 
4403dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
4404dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = size;
4405dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
4406dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
4407dc13fee6SSepherosa Ziehau 
4408dc13fee6SSepherosa Ziehau 	return (0);
4409dc13fee6SSepherosa Ziehau }
4410dc13fee6SSepherosa Ziehau 
4411dc13fee6SSepherosa Ziehau static int
4412dc13fee6SSepherosa Ziehau hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS)
4413dc13fee6SSepherosa Ziehau {
4414dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4415dc13fee6SSepherosa Ziehau 	int error, pkts;
4416dc13fee6SSepherosa Ziehau 
4417dc13fee6SSepherosa Ziehau 	pkts = sc->hn_agg_pkts;
4418dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pkts, 0, req);
4419dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
4420dc13fee6SSepherosa Ziehau 		return (error);
4421dc13fee6SSepherosa Ziehau 
4422dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
4423dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = pkts;
4424dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
4425dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
4426dc13fee6SSepherosa Ziehau 
4427dc13fee6SSepherosa Ziehau 	return (0);
4428dc13fee6SSepherosa Ziehau }
4429dc13fee6SSepherosa Ziehau 
4430dc13fee6SSepherosa Ziehau static int
4431dc13fee6SSepherosa Ziehau hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS)
4432dc13fee6SSepherosa Ziehau {
4433dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4434dc13fee6SSepherosa Ziehau 	int pkts;
4435dc13fee6SSepherosa Ziehau 
4436dc13fee6SSepherosa Ziehau 	pkts = sc->hn_tx_ring[0].hn_agg_pktmax;
4437dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &pkts, 0, req));
4438dc13fee6SSepherosa Ziehau }
4439dc13fee6SSepherosa Ziehau 
4440dc13fee6SSepherosa Ziehau static int
4441dc13fee6SSepherosa Ziehau hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS)
4442dc13fee6SSepherosa Ziehau {
4443dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4444dc13fee6SSepherosa Ziehau 	int align;
4445dc13fee6SSepherosa Ziehau 
4446dc13fee6SSepherosa Ziehau 	align = sc->hn_tx_ring[0].hn_agg_align;
4447dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &align, 0, req));
4448dc13fee6SSepherosa Ziehau }
4449dc13fee6SSepherosa Ziehau 
44506c1204dfSSepherosa Ziehau static void
44516c1204dfSSepherosa Ziehau hn_chan_polling(struct vmbus_channel *chan, u_int pollhz)
44526c1204dfSSepherosa Ziehau {
44536c1204dfSSepherosa Ziehau 	if (pollhz == 0)
44546c1204dfSSepherosa Ziehau 		vmbus_chan_poll_disable(chan);
44556c1204dfSSepherosa Ziehau 	else
44566c1204dfSSepherosa Ziehau 		vmbus_chan_poll_enable(chan, pollhz);
44576c1204dfSSepherosa Ziehau }
44586c1204dfSSepherosa Ziehau 
44596c1204dfSSepherosa Ziehau static void
44606c1204dfSSepherosa Ziehau hn_polling(struct hn_softc *sc, u_int pollhz)
44616c1204dfSSepherosa Ziehau {
44626c1204dfSSepherosa Ziehau 	int nsubch = sc->hn_rx_ring_inuse - 1;
44636c1204dfSSepherosa Ziehau 
44646c1204dfSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
44656c1204dfSSepherosa Ziehau 
44666c1204dfSSepherosa Ziehau 	if (nsubch > 0) {
44676c1204dfSSepherosa Ziehau 		struct vmbus_channel **subch;
44686c1204dfSSepherosa Ziehau 		int i;
44696c1204dfSSepherosa Ziehau 
44706c1204dfSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
44716c1204dfSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
44726c1204dfSSepherosa Ziehau 			hn_chan_polling(subch[i], pollhz);
44736c1204dfSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
44746c1204dfSSepherosa Ziehau 	}
44756c1204dfSSepherosa Ziehau 	hn_chan_polling(sc->hn_prichan, pollhz);
44766c1204dfSSepherosa Ziehau }
44776c1204dfSSepherosa Ziehau 
44786c1204dfSSepherosa Ziehau static int
44796c1204dfSSepherosa Ziehau hn_polling_sysctl(SYSCTL_HANDLER_ARGS)
44806c1204dfSSepherosa Ziehau {
44816c1204dfSSepherosa Ziehau 	struct hn_softc *sc = arg1;
44826c1204dfSSepherosa Ziehau 	int pollhz, error;
44836c1204dfSSepherosa Ziehau 
44846c1204dfSSepherosa Ziehau 	pollhz = sc->hn_pollhz;
44856c1204dfSSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pollhz, 0, req);
44866c1204dfSSepherosa Ziehau 	if (error || req->newptr == NULL)
44876c1204dfSSepherosa Ziehau 		return (error);
44886c1204dfSSepherosa Ziehau 
44896c1204dfSSepherosa Ziehau 	if (pollhz != 0 &&
44906c1204dfSSepherosa Ziehau 	    (pollhz < VMBUS_CHAN_POLLHZ_MIN || pollhz > VMBUS_CHAN_POLLHZ_MAX))
44916c1204dfSSepherosa Ziehau 		return (EINVAL);
44926c1204dfSSepherosa Ziehau 
44936c1204dfSSepherosa Ziehau 	HN_LOCK(sc);
44946c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz != pollhz) {
44956c1204dfSSepherosa Ziehau 		sc->hn_pollhz = pollhz;
4496*4db5958aSJustin Hibbits 		if ((if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) &&
44976c1204dfSSepherosa Ziehau 		    (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
44986c1204dfSSepherosa Ziehau 			hn_polling(sc, sc->hn_pollhz);
44996c1204dfSSepherosa Ziehau 	}
45006c1204dfSSepherosa Ziehau 	HN_UNLOCK(sc);
45016c1204dfSSepherosa Ziehau 
45026c1204dfSSepherosa Ziehau 	return (0);
45036c1204dfSSepherosa Ziehau }
45046c1204dfSSepherosa Ziehau 
4505dc13fee6SSepherosa Ziehau static int
450615516c77SSepherosa Ziehau hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS)
450715516c77SSepherosa Ziehau {
450815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
450915516c77SSepherosa Ziehau 	char verstr[16];
451015516c77SSepherosa Ziehau 
451115516c77SSepherosa Ziehau 	snprintf(verstr, sizeof(verstr), "%u.%u",
451215516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
451315516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
451415516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
451515516c77SSepherosa Ziehau }
451615516c77SSepherosa Ziehau 
451715516c77SSepherosa Ziehau static int
451815516c77SSepherosa Ziehau hn_caps_sysctl(SYSCTL_HANDLER_ARGS)
451915516c77SSepherosa Ziehau {
452015516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
452115516c77SSepherosa Ziehau 	char caps_str[128];
452215516c77SSepherosa Ziehau 	uint32_t caps;
452315516c77SSepherosa Ziehau 
452415516c77SSepherosa Ziehau 	HN_LOCK(sc);
452515516c77SSepherosa Ziehau 	caps = sc->hn_caps;
452615516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
452715516c77SSepherosa Ziehau 	snprintf(caps_str, sizeof(caps_str), "%b", caps, HN_CAP_BITS);
452815516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, caps_str, sizeof(caps_str), req);
452915516c77SSepherosa Ziehau }
453015516c77SSepherosa Ziehau 
453115516c77SSepherosa Ziehau static int
453215516c77SSepherosa Ziehau hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS)
453315516c77SSepherosa Ziehau {
453415516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
453515516c77SSepherosa Ziehau 	char assist_str[128];
453615516c77SSepherosa Ziehau 	uint32_t hwassist;
453715516c77SSepherosa Ziehau 
453815516c77SSepherosa Ziehau 	HN_LOCK(sc);
4539*4db5958aSJustin Hibbits 	hwassist = if_gethwassist(sc->hn_ifp);
454015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
454115516c77SSepherosa Ziehau 	snprintf(assist_str, sizeof(assist_str), "%b", hwassist, CSUM_BITS);
454215516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, assist_str, sizeof(assist_str), req);
454315516c77SSepherosa Ziehau }
454415516c77SSepherosa Ziehau 
454515516c77SSepherosa Ziehau static int
454615516c77SSepherosa Ziehau hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS)
454715516c77SSepherosa Ziehau {
454815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
454915516c77SSepherosa Ziehau 	char filter_str[128];
455015516c77SSepherosa Ziehau 	uint32_t filter;
455115516c77SSepherosa Ziehau 
455215516c77SSepherosa Ziehau 	HN_LOCK(sc);
455315516c77SSepherosa Ziehau 	filter = sc->hn_rx_filter;
455415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
455515516c77SSepherosa Ziehau 	snprintf(filter_str, sizeof(filter_str), "%b", filter,
455615516c77SSepherosa Ziehau 	    NDIS_PACKET_TYPES);
455715516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req);
455815516c77SSepherosa Ziehau }
455915516c77SSepherosa Ziehau 
456080c3eb7bSWei Hu static int
456180c3eb7bSWei Hu hn_rsc_sysctl(SYSCTL_HANDLER_ARGS)
456280c3eb7bSWei Hu {
456380c3eb7bSWei Hu 	struct hn_softc *sc = arg1;
456480c3eb7bSWei Hu 	uint32_t mtu;
456580c3eb7bSWei Hu 	int error;
456680c3eb7bSWei Hu 	HN_LOCK(sc);
456780c3eb7bSWei Hu 	error = hn_rndis_get_mtu(sc, &mtu);
456880c3eb7bSWei Hu 	if (error) {
456980c3eb7bSWei Hu 		if_printf(sc->hn_ifp, "failed to get mtu\n");
457080c3eb7bSWei Hu 		goto back;
457180c3eb7bSWei Hu 	}
457280c3eb7bSWei Hu 	error = SYSCTL_OUT(req, &(sc->hn_rsc_ctrl), sizeof(sc->hn_rsc_ctrl));
457380c3eb7bSWei Hu 	if (error || req->newptr == NULL)
457480c3eb7bSWei Hu 		goto back;
457580c3eb7bSWei Hu 
457680c3eb7bSWei Hu 	error = SYSCTL_IN(req, &(sc->hn_rsc_ctrl), sizeof(sc->hn_rsc_ctrl));
457780c3eb7bSWei Hu 	if (error)
457880c3eb7bSWei Hu 		goto back;
457980c3eb7bSWei Hu 	error = hn_rndis_reconf_offload(sc, mtu);
458080c3eb7bSWei Hu back:
458180c3eb7bSWei Hu 	HN_UNLOCK(sc);
458280c3eb7bSWei Hu 	return (error);
458380c3eb7bSWei Hu }
458434d68912SSepherosa Ziehau #ifndef RSS
458534d68912SSepherosa Ziehau 
458615516c77SSepherosa Ziehau static int
458715516c77SSepherosa Ziehau hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS)
458815516c77SSepherosa Ziehau {
458915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
459015516c77SSepherosa Ziehau 	int error;
459115516c77SSepherosa Ziehau 
459215516c77SSepherosa Ziehau 	HN_LOCK(sc);
459315516c77SSepherosa Ziehau 
459415516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
459515516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
459615516c77SSepherosa Ziehau 		goto back;
459715516c77SSepherosa Ziehau 
4598642ec226SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) ||
4599642ec226SSepherosa Ziehau 	    (hn_xpnt_vf && sc->hn_vf_ifp != NULL)) {
4600642ec226SSepherosa Ziehau 		/*
4601642ec226SSepherosa Ziehau 		 * RSS key is synchronized w/ VF's, don't allow users
4602642ec226SSepherosa Ziehau 		 * to change it.
4603642ec226SSepherosa Ziehau 		 */
4604642ec226SSepherosa Ziehau 		error = EBUSY;
4605642ec226SSepherosa Ziehau 		goto back;
4606642ec226SSepherosa Ziehau 	}
4607642ec226SSepherosa Ziehau 
460815516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
460915516c77SSepherosa Ziehau 	if (error)
461015516c77SSepherosa Ziehau 		goto back;
461115516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
461215516c77SSepherosa Ziehau 
461315516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
461415516c77SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
461515516c77SSepherosa Ziehau 	} else {
461615516c77SSepherosa Ziehau 		/* Not RSS capable, at least for now; just save the RSS key. */
461715516c77SSepherosa Ziehau 		error = 0;
461815516c77SSepherosa Ziehau 	}
461915516c77SSepherosa Ziehau back:
462015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
462115516c77SSepherosa Ziehau 	return (error);
462215516c77SSepherosa Ziehau }
462315516c77SSepherosa Ziehau 
462415516c77SSepherosa Ziehau static int
462515516c77SSepherosa Ziehau hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS)
462615516c77SSepherosa Ziehau {
462715516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
462815516c77SSepherosa Ziehau 	int error;
462915516c77SSepherosa Ziehau 
463015516c77SSepherosa Ziehau 	HN_LOCK(sc);
463115516c77SSepherosa Ziehau 
463215516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
463315516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
463415516c77SSepherosa Ziehau 		goto back;
463515516c77SSepherosa Ziehau 
463615516c77SSepherosa Ziehau 	/*
463715516c77SSepherosa Ziehau 	 * Don't allow RSS indirect table change, if this interface is not
463815516c77SSepherosa Ziehau 	 * RSS capable currently.
463915516c77SSepherosa Ziehau 	 */
464015516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
464115516c77SSepherosa Ziehau 		error = EOPNOTSUPP;
464215516c77SSepherosa Ziehau 		goto back;
464315516c77SSepherosa Ziehau 	}
464415516c77SSepherosa Ziehau 
464515516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
464615516c77SSepherosa Ziehau 	if (error)
464715516c77SSepherosa Ziehau 		goto back;
464815516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSIND;
464915516c77SSepherosa Ziehau 
4650afd4971bSSepherosa Ziehau 	hn_rss_ind_fixup(sc);
465115516c77SSepherosa Ziehau 	error = hn_rss_reconfig(sc);
465215516c77SSepherosa Ziehau back:
465315516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
465415516c77SSepherosa Ziehau 	return (error);
465515516c77SSepherosa Ziehau }
465615516c77SSepherosa Ziehau 
465734d68912SSepherosa Ziehau #endif	/* !RSS */
465834d68912SSepherosa Ziehau 
465915516c77SSepherosa Ziehau static int
466015516c77SSepherosa Ziehau hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS)
466115516c77SSepherosa Ziehau {
466215516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
466315516c77SSepherosa Ziehau 	char hash_str[128];
466415516c77SSepherosa Ziehau 	uint32_t hash;
466515516c77SSepherosa Ziehau 
466615516c77SSepherosa Ziehau 	HN_LOCK(sc);
466715516c77SSepherosa Ziehau 	hash = sc->hn_rss_hash;
466815516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
466915516c77SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
467015516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
467115516c77SSepherosa Ziehau }
467215516c77SSepherosa Ziehau 
467315516c77SSepherosa Ziehau static int
4674642ec226SSepherosa Ziehau hn_rss_hcap_sysctl(SYSCTL_HANDLER_ARGS)
4675642ec226SSepherosa Ziehau {
4676642ec226SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4677642ec226SSepherosa Ziehau 	char hash_str[128];
4678642ec226SSepherosa Ziehau 	uint32_t hash;
4679642ec226SSepherosa Ziehau 
4680642ec226SSepherosa Ziehau 	HN_LOCK(sc);
4681642ec226SSepherosa Ziehau 	hash = sc->hn_rss_hcap;
4682642ec226SSepherosa Ziehau 	HN_UNLOCK(sc);
4683642ec226SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
4684642ec226SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
4685642ec226SSepherosa Ziehau }
4686642ec226SSepherosa Ziehau 
4687642ec226SSepherosa Ziehau static int
4688642ec226SSepherosa Ziehau hn_rss_mbuf_sysctl(SYSCTL_HANDLER_ARGS)
4689642ec226SSepherosa Ziehau {
4690642ec226SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4691642ec226SSepherosa Ziehau 	char hash_str[128];
4692642ec226SSepherosa Ziehau 	uint32_t hash;
4693642ec226SSepherosa Ziehau 
4694642ec226SSepherosa Ziehau 	HN_LOCK(sc);
4695642ec226SSepherosa Ziehau 	hash = sc->hn_rx_ring[0].hn_mbuf_hash;
4696642ec226SSepherosa Ziehau 	HN_UNLOCK(sc);
4697642ec226SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
4698642ec226SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
4699642ec226SSepherosa Ziehau }
4700642ec226SSepherosa Ziehau 
4701642ec226SSepherosa Ziehau static int
470240d60d6eSDexuan Cui hn_vf_sysctl(SYSCTL_HANDLER_ARGS)
470340d60d6eSDexuan Cui {
470440d60d6eSDexuan Cui 	struct hn_softc *sc = arg1;
4705499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
4706*4db5958aSJustin Hibbits 	if_t vf_ifp;
470740d60d6eSDexuan Cui 
470840d60d6eSDexuan Cui 	HN_LOCK(sc);
470940d60d6eSDexuan Cui 	vf_name[0] = '\0';
4710962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
4711962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
4712*4db5958aSJustin Hibbits 		snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf_ifp));
471340d60d6eSDexuan Cui 	HN_UNLOCK(sc);
471440d60d6eSDexuan Cui 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
471540d60d6eSDexuan Cui }
471640d60d6eSDexuan Cui 
471740d60d6eSDexuan Cui static int
4718499c3e17SSepherosa Ziehau hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS)
4719499c3e17SSepherosa Ziehau {
4720499c3e17SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4721499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
4722*4db5958aSJustin Hibbits 	if_t vf_ifp;
4723499c3e17SSepherosa Ziehau 
4724499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
4725499c3e17SSepherosa Ziehau 	vf_name[0] = '\0';
4726962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_rx_ring[0].hn_rxvf_ifp;
4727962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
4728*4db5958aSJustin Hibbits 		snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf_ifp));
4729499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
4730499c3e17SSepherosa Ziehau 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
4731499c3e17SSepherosa Ziehau }
4732499c3e17SSepherosa Ziehau 
4733499c3e17SSepherosa Ziehau static int
4734499c3e17SSepherosa Ziehau hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
4735499c3e17SSepherosa Ziehau {
4736499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4737499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4738499c3e17SSepherosa Ziehau 	int error, i;
4739499c3e17SSepherosa Ziehau 	bool first;
4740499c3e17SSepherosa Ziehau 
4741499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4742499c3e17SSepherosa Ziehau 	if (error != 0)
4743499c3e17SSepherosa Ziehau 		return (error);
4744499c3e17SSepherosa Ziehau 
4745499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4746499c3e17SSepherosa Ziehau 	if (sb == NULL)
4747499c3e17SSepherosa Ziehau 		return (ENOMEM);
4748499c3e17SSepherosa Ziehau 
4749499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4750499c3e17SSepherosa Ziehau 
4751499c3e17SSepherosa Ziehau 	first = true;
4752499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4753d74b7baeSGleb Smirnoff 		struct epoch_tracker et;
4754*4db5958aSJustin Hibbits 		if_t ifp;
4755499c3e17SSepherosa Ziehau 
4756499c3e17SSepherosa Ziehau 		if (hn_vfmap[i] == NULL)
4757499c3e17SSepherosa Ziehau 			continue;
4758499c3e17SSepherosa Ziehau 
4759d74b7baeSGleb Smirnoff 		NET_EPOCH_ENTER(et);
4760499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4761499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4762499c3e17SSepherosa Ziehau 			if (first)
4763*4db5958aSJustin Hibbits 				sbuf_printf(sb, "%s", if_name(ifp));
4764499c3e17SSepherosa Ziehau 			else
4765*4db5958aSJustin Hibbits 				sbuf_printf(sb, " %s", if_name(ifp));
4766499c3e17SSepherosa Ziehau 			first = false;
4767499c3e17SSepherosa Ziehau 		}
4768d74b7baeSGleb Smirnoff 		NET_EPOCH_EXIT(et);
4769499c3e17SSepherosa Ziehau 	}
4770499c3e17SSepherosa Ziehau 
4771499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4772499c3e17SSepherosa Ziehau 
4773499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4774499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4775499c3e17SSepherosa Ziehau 	return (error);
4776499c3e17SSepherosa Ziehau }
4777499c3e17SSepherosa Ziehau 
4778499c3e17SSepherosa Ziehau static int
4779499c3e17SSepherosa Ziehau hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
4780499c3e17SSepherosa Ziehau {
4781499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4782499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4783499c3e17SSepherosa Ziehau 	int error, i;
4784499c3e17SSepherosa Ziehau 	bool first;
4785499c3e17SSepherosa Ziehau 
4786499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4787499c3e17SSepherosa Ziehau 	if (error != 0)
4788499c3e17SSepherosa Ziehau 		return (error);
4789499c3e17SSepherosa Ziehau 
4790499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4791499c3e17SSepherosa Ziehau 	if (sb == NULL)
4792499c3e17SSepherosa Ziehau 		return (ENOMEM);
4793499c3e17SSepherosa Ziehau 
4794499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4795499c3e17SSepherosa Ziehau 
4796499c3e17SSepherosa Ziehau 	first = true;
4797499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4798d74b7baeSGleb Smirnoff 		struct epoch_tracker et;
4799*4db5958aSJustin Hibbits 		if_t ifp, hn_ifp;
4800499c3e17SSepherosa Ziehau 
4801499c3e17SSepherosa Ziehau 		hn_ifp = hn_vfmap[i];
4802499c3e17SSepherosa Ziehau 		if (hn_ifp == NULL)
4803499c3e17SSepherosa Ziehau 			continue;
4804499c3e17SSepherosa Ziehau 
4805d74b7baeSGleb Smirnoff 		NET_EPOCH_ENTER(et);
4806499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4807499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4808499c3e17SSepherosa Ziehau 			if (first) {
4809*4db5958aSJustin Hibbits 				sbuf_printf(sb, "%s:%s", if_name(ifp),
4810*4db5958aSJustin Hibbits 				    if_name(hn_ifp));
4811499c3e17SSepherosa Ziehau 			} else {
4812*4db5958aSJustin Hibbits 				sbuf_printf(sb, " %s:%s", if_name(ifp),
4813*4db5958aSJustin Hibbits 				    if_name(hn_ifp));
4814499c3e17SSepherosa Ziehau 			}
4815499c3e17SSepherosa Ziehau 			first = false;
4816499c3e17SSepherosa Ziehau 		}
4817d74b7baeSGleb Smirnoff 		NET_EPOCH_EXIT(et);
4818499c3e17SSepherosa Ziehau 	}
4819499c3e17SSepherosa Ziehau 
4820499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4821499c3e17SSepherosa Ziehau 
4822499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4823499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4824499c3e17SSepherosa Ziehau 	return (error);
4825499c3e17SSepherosa Ziehau }
4826499c3e17SSepherosa Ziehau 
4827499c3e17SSepherosa Ziehau static int
48289c6cae24SSepherosa Ziehau hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS)
48299c6cae24SSepherosa Ziehau {
48309c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
48319c6cae24SSepherosa Ziehau 	int error, onoff = 0;
48329c6cae24SSepherosa Ziehau 
48339c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF)
48349c6cae24SSepherosa Ziehau 		onoff = 1;
48359c6cae24SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &onoff, 0, req);
48369c6cae24SSepherosa Ziehau 	if (error || req->newptr == NULL)
48379c6cae24SSepherosa Ziehau 		return (error);
48389c6cae24SSepherosa Ziehau 
48399c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
48409c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit() */
48419c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
48429c6cae24SSepherosa Ziehau 	if (onoff)
48439c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
48449c6cae24SSepherosa Ziehau 	else
48459c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags &= ~HN_XVFFLAG_ACCBPF;
48469c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
48479c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
48489c6cae24SSepherosa Ziehau 
48499c6cae24SSepherosa Ziehau 	return (0);
48509c6cae24SSepherosa Ziehau }
48519c6cae24SSepherosa Ziehau 
48529c6cae24SSepherosa Ziehau static int
48539c6cae24SSepherosa Ziehau hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS)
48549c6cae24SSepherosa Ziehau {
48559c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
48569c6cae24SSepherosa Ziehau 	int enabled = 0;
48579c6cae24SSepherosa Ziehau 
48589c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
48599c6cae24SSepherosa Ziehau 		enabled = 1;
48609c6cae24SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &enabled, 0, req));
48619c6cae24SSepherosa Ziehau }
48629c6cae24SSepherosa Ziehau 
48639c6cae24SSepherosa Ziehau static int
486415516c77SSepherosa Ziehau hn_check_iplen(const struct mbuf *m, int hoff)
486515516c77SSepherosa Ziehau {
486615516c77SSepherosa Ziehau 	const struct ip *ip;
486715516c77SSepherosa Ziehau 	int len, iphlen, iplen;
486815516c77SSepherosa Ziehau 	const struct tcphdr *th;
486915516c77SSepherosa Ziehau 	int thoff;				/* TCP data offset */
487015516c77SSepherosa Ziehau 
487115516c77SSepherosa Ziehau 	len = hoff + sizeof(struct ip);
487215516c77SSepherosa Ziehau 
487315516c77SSepherosa Ziehau 	/* The packet must be at least the size of an IP header. */
487415516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < len)
487515516c77SSepherosa Ziehau 		return IPPROTO_DONE;
487615516c77SSepherosa Ziehau 
487715516c77SSepherosa Ziehau 	/* The fixed IP header must reside completely in the first mbuf. */
487815516c77SSepherosa Ziehau 	if (m->m_len < len)
487915516c77SSepherosa Ziehau 		return IPPROTO_DONE;
488015516c77SSepherosa Ziehau 
488115516c77SSepherosa Ziehau 	ip = mtodo(m, hoff);
488215516c77SSepherosa Ziehau 
488315516c77SSepherosa Ziehau 	/* Bound check the packet's stated IP header length. */
488415516c77SSepherosa Ziehau 	iphlen = ip->ip_hl << 2;
488515516c77SSepherosa Ziehau 	if (iphlen < sizeof(struct ip))		/* minimum header length */
488615516c77SSepherosa Ziehau 		return IPPROTO_DONE;
488715516c77SSepherosa Ziehau 
488815516c77SSepherosa Ziehau 	/* The full IP header must reside completely in the one mbuf. */
488915516c77SSepherosa Ziehau 	if (m->m_len < hoff + iphlen)
489015516c77SSepherosa Ziehau 		return IPPROTO_DONE;
489115516c77SSepherosa Ziehau 
489215516c77SSepherosa Ziehau 	iplen = ntohs(ip->ip_len);
489315516c77SSepherosa Ziehau 
489415516c77SSepherosa Ziehau 	/*
489515516c77SSepherosa Ziehau 	 * Check that the amount of data in the buffers is as
489615516c77SSepherosa Ziehau 	 * at least much as the IP header would have us expect.
489715516c77SSepherosa Ziehau 	 */
489815516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < hoff + iplen)
489915516c77SSepherosa Ziehau 		return IPPROTO_DONE;
490015516c77SSepherosa Ziehau 
490115516c77SSepherosa Ziehau 	/*
490215516c77SSepherosa Ziehau 	 * Ignore IP fragments.
490315516c77SSepherosa Ziehau 	 */
490415516c77SSepherosa Ziehau 	if (ntohs(ip->ip_off) & (IP_OFFMASK | IP_MF))
490515516c77SSepherosa Ziehau 		return IPPROTO_DONE;
490615516c77SSepherosa Ziehau 
490715516c77SSepherosa Ziehau 	/*
490815516c77SSepherosa Ziehau 	 * The TCP/IP or UDP/IP header must be entirely contained within
490915516c77SSepherosa Ziehau 	 * the first fragment of a packet.
491015516c77SSepherosa Ziehau 	 */
491115516c77SSepherosa Ziehau 	switch (ip->ip_p) {
491215516c77SSepherosa Ziehau 	case IPPROTO_TCP:
491315516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct tcphdr))
491415516c77SSepherosa Ziehau 			return IPPROTO_DONE;
491515516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct tcphdr))
491615516c77SSepherosa Ziehau 			return IPPROTO_DONE;
491715516c77SSepherosa Ziehau 		th = (const struct tcphdr *)((const uint8_t *)ip + iphlen);
491815516c77SSepherosa Ziehau 		thoff = th->th_off << 2;
491915516c77SSepherosa Ziehau 		if (thoff < sizeof(struct tcphdr) || thoff + iphlen > iplen)
492015516c77SSepherosa Ziehau 			return IPPROTO_DONE;
492115516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + thoff)
492215516c77SSepherosa Ziehau 			return IPPROTO_DONE;
492315516c77SSepherosa Ziehau 		break;
492415516c77SSepherosa Ziehau 	case IPPROTO_UDP:
492515516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct udphdr))
492615516c77SSepherosa Ziehau 			return IPPROTO_DONE;
492715516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct udphdr))
492815516c77SSepherosa Ziehau 			return IPPROTO_DONE;
492915516c77SSepherosa Ziehau 		break;
493015516c77SSepherosa Ziehau 	default:
493115516c77SSepherosa Ziehau 		if (iplen < iphlen)
493215516c77SSepherosa Ziehau 			return IPPROTO_DONE;
493315516c77SSepherosa Ziehau 		break;
493415516c77SSepherosa Ziehau 	}
493515516c77SSepherosa Ziehau 	return ip->ip_p;
493615516c77SSepherosa Ziehau }
493715516c77SSepherosa Ziehau 
4938db76829bSSepherosa Ziehau static void
4939db76829bSSepherosa Ziehau hn_rxpkt_proto(const struct mbuf *m_new, int *l3proto, int *l4proto)
4940db76829bSSepherosa Ziehau {
4941db76829bSSepherosa Ziehau 	const struct ether_header *eh;
4942db76829bSSepherosa Ziehau 	uint16_t etype;
4943db76829bSSepherosa Ziehau 	int hoff;
4944db76829bSSepherosa Ziehau 
4945db76829bSSepherosa Ziehau 	hoff = sizeof(*eh);
4946db76829bSSepherosa Ziehau 	/* Checked at the beginning of this function. */
4947db76829bSSepherosa Ziehau 	KASSERT(m_new->m_len >= hoff, ("not ethernet frame"));
4948db76829bSSepherosa Ziehau 
4949db76829bSSepherosa Ziehau 	eh = mtod(m_new, const struct ether_header *);
4950db76829bSSepherosa Ziehau 	etype = ntohs(eh->ether_type);
4951db76829bSSepherosa Ziehau 	if (etype == ETHERTYPE_VLAN) {
4952db76829bSSepherosa Ziehau 		const struct ether_vlan_header *evl;
4953db76829bSSepherosa Ziehau 
4954db76829bSSepherosa Ziehau 		hoff = sizeof(*evl);
4955db76829bSSepherosa Ziehau 		if (m_new->m_len < hoff)
4956db76829bSSepherosa Ziehau 			return;
4957db76829bSSepherosa Ziehau 		evl = mtod(m_new, const struct ether_vlan_header *);
4958db76829bSSepherosa Ziehau 		etype = ntohs(evl->evl_proto);
4959db76829bSSepherosa Ziehau 	}
4960db76829bSSepherosa Ziehau 	*l3proto = etype;
4961db76829bSSepherosa Ziehau 
4962db76829bSSepherosa Ziehau 	if (etype == ETHERTYPE_IP)
4963db76829bSSepherosa Ziehau 		*l4proto = hn_check_iplen(m_new, hoff);
4964db76829bSSepherosa Ziehau 	else
4965db76829bSSepherosa Ziehau 		*l4proto = IPPROTO_DONE;
4966db76829bSSepherosa Ziehau }
4967db76829bSSepherosa Ziehau 
496815516c77SSepherosa Ziehau static int
496915516c77SSepherosa Ziehau hn_create_rx_data(struct hn_softc *sc, int ring_cnt)
497015516c77SSepherosa Ziehau {
497115516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
497215516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
497315516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
497415516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
497515516c77SSepherosa Ziehau 	int lroent_cnt;
497615516c77SSepherosa Ziehau #endif
497715516c77SSepherosa Ziehau 	int i;
497815516c77SSepherosa Ziehau 
497915516c77SSepherosa Ziehau 	/*
498015516c77SSepherosa Ziehau 	 * Create RXBUF for reception.
498115516c77SSepherosa Ziehau 	 *
498215516c77SSepherosa Ziehau 	 * NOTE:
498315516c77SSepherosa Ziehau 	 * - It is shared by all channels.
498415516c77SSepherosa Ziehau 	 * - A large enough buffer is allocated, certain version of NVSes
498515516c77SSepherosa Ziehau 	 *   may further limit the usable space.
498615516c77SSepherosa Ziehau 	 */
498715516c77SSepherosa Ziehau 	sc->hn_rxbuf = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
498815516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_RXBUF_SIZE, &sc->hn_rxbuf_dma,
498915516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
499015516c77SSepherosa Ziehau 	if (sc->hn_rxbuf == NULL) {
499115516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate rxbuf failed\n");
499215516c77SSepherosa Ziehau 		return (ENOMEM);
499315516c77SSepherosa Ziehau 	}
499415516c77SSepherosa Ziehau 
499515516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = ring_cnt;
499615516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = sc->hn_rx_ring_cnt;
499715516c77SSepherosa Ziehau 
499815516c77SSepherosa Ziehau 	sc->hn_rx_ring = malloc(sizeof(struct hn_rx_ring) * sc->hn_rx_ring_cnt,
499915516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
500015516c77SSepherosa Ziehau 
500115516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
500215516c77SSepherosa Ziehau 	lroent_cnt = hn_lro_entry_count;
500315516c77SSepherosa Ziehau 	if (lroent_cnt < TCP_LRO_ENTRIES)
500415516c77SSepherosa Ziehau 		lroent_cnt = TCP_LRO_ENTRIES;
500515516c77SSepherosa Ziehau 	if (bootverbose)
500615516c77SSepherosa Ziehau 		device_printf(dev, "LRO: entry count %d\n", lroent_cnt);
500715516c77SSepherosa Ziehau #endif	/* INET || INET6 */
500815516c77SSepherosa Ziehau 
500915516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
501015516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
501115516c77SSepherosa Ziehau 
501215516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.rx sysctl tree */
501315516c77SSepherosa Ziehau 	sc->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rx",
501415516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
501515516c77SSepherosa Ziehau 
501615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
501715516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
501815516c77SSepherosa Ziehau 
501915516c77SSepherosa Ziehau 		rxr->hn_br = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
502015516c77SSepherosa Ziehau 		    PAGE_SIZE, 0, HN_TXBR_SIZE + HN_RXBR_SIZE,
502115516c77SSepherosa Ziehau 		    &rxr->hn_br_dma, BUS_DMA_WAITOK);
502215516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL) {
502315516c77SSepherosa Ziehau 			device_printf(dev, "allocate bufring failed\n");
502415516c77SSepherosa Ziehau 			return (ENOMEM);
502515516c77SSepherosa Ziehau 		}
502615516c77SSepherosa Ziehau 
502715516c77SSepherosa Ziehau 		if (hn_trust_hosttcp)
502815516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_TCP;
502915516c77SSepherosa Ziehau 		if (hn_trust_hostudp)
503015516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_UDP;
503115516c77SSepherosa Ziehau 		if (hn_trust_hostip)
503215516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_IP;
5033642ec226SSepherosa Ziehau 		rxr->hn_mbuf_hash = NDIS_HASH_ALL;
503415516c77SSepherosa Ziehau 		rxr->hn_ifp = sc->hn_ifp;
503515516c77SSepherosa Ziehau 		if (i < sc->hn_tx_ring_cnt)
503615516c77SSepherosa Ziehau 			rxr->hn_txr = &sc->hn_tx_ring[i];
503715516c77SSepherosa Ziehau 		rxr->hn_pktbuf_len = HN_PKTBUF_LEN_DEF;
503815516c77SSepherosa Ziehau 		rxr->hn_pktbuf = malloc(rxr->hn_pktbuf_len, M_DEVBUF, M_WAITOK);
503915516c77SSepherosa Ziehau 		rxr->hn_rx_idx = i;
504015516c77SSepherosa Ziehau 		rxr->hn_rxbuf = sc->hn_rxbuf;
504115516c77SSepherosa Ziehau 
504215516c77SSepherosa Ziehau 		/*
504315516c77SSepherosa Ziehau 		 * Initialize LRO.
504415516c77SSepherosa Ziehau 		 */
504515516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
504615516c77SSepherosa Ziehau 		tcp_lro_init_args(&rxr->hn_lro, sc->hn_ifp, lroent_cnt,
504715516c77SSepherosa Ziehau 		    hn_lro_mbufq_depth);
504815516c77SSepherosa Ziehau 		rxr->hn_lro.lro_length_lim = HN_LRO_LENLIM_DEF;
504915516c77SSepherosa Ziehau 		rxr->hn_lro.lro_ackcnt_lim = HN_LRO_ACKCNT_DEF;
505015516c77SSepherosa Ziehau #endif	/* INET || INET6 */
505115516c77SSepherosa Ziehau 
505215516c77SSepherosa Ziehau 		if (sc->hn_rx_sysctl_tree != NULL) {
505315516c77SSepherosa Ziehau 			char name[16];
505415516c77SSepherosa Ziehau 
505515516c77SSepherosa Ziehau 			/*
505615516c77SSepherosa Ziehau 			 * Create per RX ring sysctl tree:
505715516c77SSepherosa Ziehau 			 * dev.hn.UNIT.rx.RINGID
505815516c77SSepherosa Ziehau 			 */
505915516c77SSepherosa Ziehau 			snprintf(name, sizeof(name), "%d", i);
506015516c77SSepherosa Ziehau 			rxr->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx,
506115516c77SSepherosa Ziehau 			    SYSCTL_CHILDREN(sc->hn_rx_sysctl_tree),
506215516c77SSepherosa Ziehau 			    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
506315516c77SSepherosa Ziehau 
506415516c77SSepherosa Ziehau 			if (rxr->hn_rx_sysctl_tree != NULL) {
506515516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
506615516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5067db0ac6deSCy Schubert 				    OID_AUTO, "packets",
5068db0ac6deSCy Schubert 				    CTLFLAG_RW | CTLFLAG_STATS, &rxr->hn_pkts,
5069db0ac6deSCy Schubert 				    "# of packets received");
507015516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
507115516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5072db0ac6deSCy Schubert 				    OID_AUTO, "rss_pkts",
5073db0ac6deSCy Schubert 				    CTLFLAG_RW | CTLFLAG_STATS,
507415516c77SSepherosa Ziehau 				    &rxr->hn_rss_pkts,
507515516c77SSepherosa Ziehau 				    "# of packets w/ RSS info received");
5076a491581fSWei Hu 				SYSCTL_ADD_ULONG(ctx,
5077a491581fSWei Hu 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5078db0ac6deSCy Schubert 				    OID_AUTO, "rsc_pkts",
5079db0ac6deSCy Schubert 				    CTLFLAG_RW | CTLFLAG_STATS,
5080a491581fSWei Hu 				    &rxr->hn_rsc_pkts,
5081a491581fSWei Hu 				    "# of RSC packets received");
5082a491581fSWei Hu 				SYSCTL_ADD_ULONG(ctx,
5083a491581fSWei Hu 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5084db0ac6deSCy Schubert 				    OID_AUTO, "rsc_drop",
5085db0ac6deSCy Schubert 				    CTLFLAG_RW | CTLFLAG_STATS,
5086a491581fSWei Hu 				    &rxr->hn_rsc_drop,
5087a491581fSWei Hu 				    "# of RSC fragments dropped");
508815516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx,
508915516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
509015516c77SSepherosa Ziehau 				    OID_AUTO, "pktbuf_len", CTLFLAG_RD,
509115516c77SSepherosa Ziehau 				    &rxr->hn_pktbuf_len, 0,
509215516c77SSepherosa Ziehau 				    "Temporary channel packet buffer length");
509315516c77SSepherosa Ziehau 			}
509415516c77SSepherosa Ziehau 		}
509515516c77SSepherosa Ziehau 	}
509615516c77SSepherosa Ziehau 
509715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_queued",
5098db0ac6deSCy Schubert 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
509915516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_queued),
510015516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
510115516c77SSepherosa Ziehau 	    "LU", "LRO queued");
510215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_flushed",
5103db0ac6deSCy Schubert 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
510415516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_flushed),
510515516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
510615516c77SSepherosa Ziehau 	    "LU", "LRO flushed");
510715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_tried",
5108db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
510915516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro_tried),
511015516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of LRO tries");
511115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_length_lim",
511215516c77SSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
511315516c77SSepherosa Ziehau 	    hn_lro_lenlim_sysctl, "IU",
511415516c77SSepherosa Ziehau 	    "Max # of data bytes to be aggregated by LRO");
511515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_ackcnt_lim",
511615516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
511715516c77SSepherosa Ziehau 	    hn_lro_ackcnt_sysctl, "I",
511815516c77SSepherosa Ziehau 	    "Max # of ACKs to be aggregated by LRO");
511915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hosttcp",
512015516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_TCP,
512115516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
5122b15a632cSGordon Bergling 	    "Trust tcp segment verification on host side, "
512315516c77SSepherosa Ziehau 	    "when csum info is missing");
512415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostudp",
512515516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_UDP,
512615516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
512715516c77SSepherosa Ziehau 	    "Trust udp datagram verification on host side, "
512815516c77SSepherosa Ziehau 	    "when csum info is missing");
512915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostip",
513015516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_IP,
513115516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
513215516c77SSepherosa Ziehau 	    "Trust ip packet verification on host side, "
513315516c77SSepherosa Ziehau 	    "when csum info is missing");
513415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_ip",
5135db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
513615516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_ip),
513715516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM IP");
513815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_tcp",
5139db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
514015516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_tcp),
514115516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM TCP");
514215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_udp",
5143db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
514415516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_udp),
514515516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM UDP");
514615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_trusted",
514715516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
514815516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_trusted),
514915516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU",
515015516c77SSepherosa Ziehau 	    "# of packets that we trust host's csum verification");
515115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "small_pkts",
5152db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
515315516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_small_pkts),
515415516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of small packets received");
515515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_ack_failed",
5156db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS , sc,
515715516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_ack_failed),
515815516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of RXBUF ack failures");
515915516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_cnt",
516015516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_cnt, 0, "# created RX rings");
516115516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_inuse",
516215516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_inuse, 0, "# used RX rings");
516315516c77SSepherosa Ziehau 
516415516c77SSepherosa Ziehau 	return (0);
516515516c77SSepherosa Ziehau }
516615516c77SSepherosa Ziehau 
516715516c77SSepherosa Ziehau static void
516815516c77SSepherosa Ziehau hn_destroy_rx_data(struct hn_softc *sc)
516915516c77SSepherosa Ziehau {
517015516c77SSepherosa Ziehau 	int i;
517115516c77SSepherosa Ziehau 
517215516c77SSepherosa Ziehau 	if (sc->hn_rxbuf != NULL) {
51732494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXBUF_REF) == 0)
517415516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_rxbuf_dma, sc->hn_rxbuf);
51752494d735SSepherosa Ziehau 		else
51762494d735SSepherosa Ziehau 			device_printf(sc->hn_dev, "RXBUF is referenced\n");
517715516c77SSepherosa Ziehau 		sc->hn_rxbuf = NULL;
517815516c77SSepherosa Ziehau 	}
517915516c77SSepherosa Ziehau 
518015516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_cnt == 0)
518115516c77SSepherosa Ziehau 		return;
518215516c77SSepherosa Ziehau 
518315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
518415516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
518515516c77SSepherosa Ziehau 
518615516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL)
518715516c77SSepherosa Ziehau 			continue;
51882494d735SSepherosa Ziehau 		if ((rxr->hn_rx_flags & HN_RX_FLAG_BR_REF) == 0) {
518915516c77SSepherosa Ziehau 			hyperv_dmamem_free(&rxr->hn_br_dma, rxr->hn_br);
51902494d735SSepherosa Ziehau 		} else {
51912494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
51922494d735SSepherosa Ziehau 			    "%dth channel bufring is referenced", i);
51932494d735SSepherosa Ziehau 		}
519415516c77SSepherosa Ziehau 		rxr->hn_br = NULL;
519515516c77SSepherosa Ziehau 
519615516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
519715516c77SSepherosa Ziehau 		tcp_lro_free(&rxr->hn_lro);
519815516c77SSepherosa Ziehau #endif
519915516c77SSepherosa Ziehau 		free(rxr->hn_pktbuf, M_DEVBUF);
520015516c77SSepherosa Ziehau 	}
520115516c77SSepherosa Ziehau 	free(sc->hn_rx_ring, M_DEVBUF);
520215516c77SSepherosa Ziehau 	sc->hn_rx_ring = NULL;
520315516c77SSepherosa Ziehau 
520415516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = 0;
520515516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = 0;
520615516c77SSepherosa Ziehau }
520715516c77SSepherosa Ziehau 
520815516c77SSepherosa Ziehau static int
520915516c77SSepherosa Ziehau hn_tx_ring_create(struct hn_softc *sc, int id)
521015516c77SSepherosa Ziehau {
521115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[id];
521215516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
521315516c77SSepherosa Ziehau 	bus_dma_tag_t parent_dtag;
521415516c77SSepherosa Ziehau 	int error, i;
521515516c77SSepherosa Ziehau 
521615516c77SSepherosa Ziehau 	txr->hn_sc = sc;
521715516c77SSepherosa Ziehau 	txr->hn_tx_idx = id;
521815516c77SSepherosa Ziehau 
521915516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
522015516c77SSepherosa Ziehau 	mtx_init(&txr->hn_txlist_spin, "hn txlist", NULL, MTX_SPIN);
522115516c77SSepherosa Ziehau #endif
522215516c77SSepherosa Ziehau 	mtx_init(&txr->hn_tx_lock, "hn tx", NULL, MTX_DEF);
522315516c77SSepherosa Ziehau 
522415516c77SSepherosa Ziehau 	txr->hn_txdesc_cnt = HN_TX_DESC_CNT;
522515516c77SSepherosa Ziehau 	txr->hn_txdesc = malloc(sizeof(struct hn_txdesc) * txr->hn_txdesc_cnt,
522615516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
522715516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
522815516c77SSepherosa Ziehau 	SLIST_INIT(&txr->hn_txlist);
522915516c77SSepherosa Ziehau #else
523015516c77SSepherosa Ziehau 	txr->hn_txdesc_br = buf_ring_alloc(txr->hn_txdesc_cnt, M_DEVBUF,
523115516c77SSepherosa Ziehau 	    M_WAITOK, &txr->hn_tx_lock);
523215516c77SSepherosa Ziehau #endif
523315516c77SSepherosa Ziehau 
52340e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_EVTTQ) {
52350e11868dSSepherosa Ziehau 		txr->hn_tx_taskq = VMBUS_GET_EVENT_TASKQ(
52360e11868dSSepherosa Ziehau 		    device_get_parent(dev), dev, HN_RING_IDX2CPU(sc, id));
52370e11868dSSepherosa Ziehau 	} else {
5238fdd0222aSSepherosa Ziehau 		txr->hn_tx_taskq = sc->hn_tx_taskqs[id % hn_tx_taskq_cnt];
52390e11868dSSepherosa Ziehau 	}
524015516c77SSepherosa Ziehau 
524123bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
524215516c77SSepherosa Ziehau 	if (hn_use_if_start) {
524315516c77SSepherosa Ziehau 		txr->hn_txeof = hn_start_txeof;
524415516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_start_taskfunc, txr);
524515516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_start_txeof_taskfunc, txr);
524623bf9e15SSepherosa Ziehau 	} else
524723bf9e15SSepherosa Ziehau #endif
524823bf9e15SSepherosa Ziehau 	{
524915516c77SSepherosa Ziehau 		int br_depth;
525015516c77SSepherosa Ziehau 
525115516c77SSepherosa Ziehau 		txr->hn_txeof = hn_xmit_txeof;
525215516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_xmit_taskfunc, txr);
525315516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_xmit_txeof_taskfunc, txr);
525415516c77SSepherosa Ziehau 
525515516c77SSepherosa Ziehau 		br_depth = hn_get_txswq_depth(txr);
525615516c77SSepherosa Ziehau 		txr->hn_mbuf_br = buf_ring_alloc(br_depth, M_DEVBUF,
525715516c77SSepherosa Ziehau 		    M_WAITOK, &txr->hn_tx_lock);
525815516c77SSepherosa Ziehau 	}
525915516c77SSepherosa Ziehau 
526015516c77SSepherosa Ziehau 	txr->hn_direct_tx_size = hn_direct_tx_size;
526115516c77SSepherosa Ziehau 
526215516c77SSepherosa Ziehau 	/*
526315516c77SSepherosa Ziehau 	 * Always schedule transmission instead of trying to do direct
526415516c77SSepherosa Ziehau 	 * transmission.  This one gives the best performance so far.
526515516c77SSepherosa Ziehau 	 */
526615516c77SSepherosa Ziehau 	txr->hn_sched_tx = 1;
526715516c77SSepherosa Ziehau 
526815516c77SSepherosa Ziehau 	parent_dtag = bus_get_dma_tag(dev);
526915516c77SSepherosa Ziehau 
527015516c77SSepherosa Ziehau 	/* DMA tag for RNDIS packet messages. */
527115516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
527215516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_ALIGN,		/* alignment */
527315516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_BOUNDARY,	/* boundary */
527415516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
527515516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
527615516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
527715516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsize */
527815516c77SSepherosa Ziehau 	    1,				/* nsegments */
527915516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsegsize */
528015516c77SSepherosa Ziehau 	    0,				/* flags */
528115516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
528215516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
528315516c77SSepherosa Ziehau 	    &txr->hn_tx_rndis_dtag);
528415516c77SSepherosa Ziehau 	if (error) {
528515516c77SSepherosa Ziehau 		device_printf(dev, "failed to create rndis dmatag\n");
528615516c77SSepherosa Ziehau 		return error;
528715516c77SSepherosa Ziehau 	}
528815516c77SSepherosa Ziehau 
528915516c77SSepherosa Ziehau 	/* DMA tag for data. */
529015516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
529115516c77SSepherosa Ziehau 	    1,				/* alignment */
529215516c77SSepherosa Ziehau 	    HN_TX_DATA_BOUNDARY,	/* boundary */
529315516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
529415516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
529515516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
529615516c77SSepherosa Ziehau 	    HN_TX_DATA_MAXSIZE,		/* maxsize */
529715516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGCNT_MAX,	/* nsegments */
529815516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGSIZE,		/* maxsegsize */
529915516c77SSepherosa Ziehau 	    0,				/* flags */
530015516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
530115516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
530215516c77SSepherosa Ziehau 	    &txr->hn_tx_data_dtag);
530315516c77SSepherosa Ziehau 	if (error) {
530415516c77SSepherosa Ziehau 		device_printf(dev, "failed to create data dmatag\n");
530515516c77SSepherosa Ziehau 		return error;
530615516c77SSepherosa Ziehau 	}
530715516c77SSepherosa Ziehau 
530815516c77SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i) {
530915516c77SSepherosa Ziehau 		struct hn_txdesc *txd = &txr->hn_txdesc[i];
531015516c77SSepherosa Ziehau 
531115516c77SSepherosa Ziehau 		txd->txr = txr;
531215516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
5313dc13fee6SSepherosa Ziehau 		STAILQ_INIT(&txd->agg_list);
531415516c77SSepherosa Ziehau 
531515516c77SSepherosa Ziehau 		/*
531615516c77SSepherosa Ziehau 		 * Allocate and load RNDIS packet message.
531715516c77SSepherosa Ziehau 		 */
531815516c77SSepherosa Ziehau         	error = bus_dmamem_alloc(txr->hn_tx_rndis_dtag,
531915516c77SSepherosa Ziehau 		    (void **)&txd->rndis_pkt,
532015516c77SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
532115516c77SSepherosa Ziehau 		    &txd->rndis_pkt_dmap);
532215516c77SSepherosa Ziehau 		if (error) {
532315516c77SSepherosa Ziehau 			device_printf(dev,
532415516c77SSepherosa Ziehau 			    "failed to allocate rndis_packet_msg, %d\n", i);
532515516c77SSepherosa Ziehau 			return error;
532615516c77SSepherosa Ziehau 		}
532715516c77SSepherosa Ziehau 
532815516c77SSepherosa Ziehau 		error = bus_dmamap_load(txr->hn_tx_rndis_dtag,
532915516c77SSepherosa Ziehau 		    txd->rndis_pkt_dmap,
533015516c77SSepherosa Ziehau 		    txd->rndis_pkt, HN_RNDIS_PKT_LEN,
533115516c77SSepherosa Ziehau 		    hyperv_dma_map_paddr, &txd->rndis_pkt_paddr,
533215516c77SSepherosa Ziehau 		    BUS_DMA_NOWAIT);
533315516c77SSepherosa Ziehau 		if (error) {
533415516c77SSepherosa Ziehau 			device_printf(dev,
533515516c77SSepherosa Ziehau 			    "failed to load rndis_packet_msg, %d\n", i);
533615516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
533715516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
533815516c77SSepherosa Ziehau 			return error;
533915516c77SSepherosa Ziehau 		}
534015516c77SSepherosa Ziehau 
534115516c77SSepherosa Ziehau 		/* DMA map for TX data. */
534215516c77SSepherosa Ziehau 		error = bus_dmamap_create(txr->hn_tx_data_dtag, 0,
534315516c77SSepherosa Ziehau 		    &txd->data_dmap);
534415516c77SSepherosa Ziehau 		if (error) {
534515516c77SSepherosa Ziehau 			device_printf(dev,
534615516c77SSepherosa Ziehau 			    "failed to allocate tx data dmamap\n");
534715516c77SSepherosa Ziehau 			bus_dmamap_unload(txr->hn_tx_rndis_dtag,
534815516c77SSepherosa Ziehau 			    txd->rndis_pkt_dmap);
534915516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
535015516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
535115516c77SSepherosa Ziehau 			return error;
535215516c77SSepherosa Ziehau 		}
535315516c77SSepherosa Ziehau 
535415516c77SSepherosa Ziehau 		/* All set, put it to list */
535515516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_ONLIST;
535615516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
535715516c77SSepherosa Ziehau 		SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
535815516c77SSepherosa Ziehau #else
535915516c77SSepherosa Ziehau 		buf_ring_enqueue(txr->hn_txdesc_br, txd);
536015516c77SSepherosa Ziehau #endif
536115516c77SSepherosa Ziehau 	}
536215516c77SSepherosa Ziehau 	txr->hn_txdesc_avail = txr->hn_txdesc_cnt;
536315516c77SSepherosa Ziehau 
536415516c77SSepherosa Ziehau 	if (sc->hn_tx_sysctl_tree != NULL) {
536515516c77SSepherosa Ziehau 		struct sysctl_oid_list *child;
536615516c77SSepherosa Ziehau 		struct sysctl_ctx_list *ctx;
536715516c77SSepherosa Ziehau 		char name[16];
536815516c77SSepherosa Ziehau 
536915516c77SSepherosa Ziehau 		/*
537015516c77SSepherosa Ziehau 		 * Create per TX ring sysctl tree:
537115516c77SSepherosa Ziehau 		 * dev.hn.UNIT.tx.RINGID
537215516c77SSepherosa Ziehau 		 */
537315516c77SSepherosa Ziehau 		ctx = device_get_sysctl_ctx(dev);
537415516c77SSepherosa Ziehau 		child = SYSCTL_CHILDREN(sc->hn_tx_sysctl_tree);
537515516c77SSepherosa Ziehau 
537615516c77SSepherosa Ziehau 		snprintf(name, sizeof(name), "%d", id);
537715516c77SSepherosa Ziehau 		txr->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
537815516c77SSepherosa Ziehau 		    name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
537915516c77SSepherosa Ziehau 
538015516c77SSepherosa Ziehau 		if (txr->hn_tx_sysctl_tree != NULL) {
538115516c77SSepherosa Ziehau 			child = SYSCTL_CHILDREN(txr->hn_tx_sysctl_tree);
538215516c77SSepherosa Ziehau 
538385e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
538415516c77SSepherosa Ziehau 			SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_avail",
538515516c77SSepherosa Ziehau 			    CTLFLAG_RD, &txr->hn_txdesc_avail, 0,
538615516c77SSepherosa Ziehau 			    "# of available TX descs");
538785e4ae1eSSepherosa Ziehau #endif
538823bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
538923bf9e15SSepherosa Ziehau 			if (!hn_use_if_start)
539023bf9e15SSepherosa Ziehau #endif
539123bf9e15SSepherosa Ziehau 			{
539215516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx, child, OID_AUTO, "oactive",
539315516c77SSepherosa Ziehau 				    CTLFLAG_RD, &txr->hn_oactive, 0,
539415516c77SSepherosa Ziehau 				    "over active");
539515516c77SSepherosa Ziehau 			}
539615516c77SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "packets",
5397db0ac6deSCy Schubert 			    CTLFLAG_RW | CTLFLAG_STATS, &txr->hn_pkts,
539815516c77SSepherosa Ziehau 			    "# of packets transmitted");
5399dc13fee6SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "sends",
5400db0ac6deSCy Schubert 			    CTLFLAG_RW | CTLFLAG_STATS, &txr->hn_sends,
5401db0ac6deSCy Schubert 			    "# of sends");
540215516c77SSepherosa Ziehau 		}
540315516c77SSepherosa Ziehau 	}
540415516c77SSepherosa Ziehau 
540515516c77SSepherosa Ziehau 	return 0;
540615516c77SSepherosa Ziehau }
540715516c77SSepherosa Ziehau 
540815516c77SSepherosa Ziehau static void
540915516c77SSepherosa Ziehau hn_txdesc_dmamap_destroy(struct hn_txdesc *txd)
541015516c77SSepherosa Ziehau {
541115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = txd->txr;
541215516c77SSepherosa Ziehau 
541315516c77SSepherosa Ziehau 	KASSERT(txd->m == NULL, ("still has mbuf installed"));
541415516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("still dma mapped"));
541515516c77SSepherosa Ziehau 
541615516c77SSepherosa Ziehau 	bus_dmamap_unload(txr->hn_tx_rndis_dtag, txd->rndis_pkt_dmap);
541715516c77SSepherosa Ziehau 	bus_dmamem_free(txr->hn_tx_rndis_dtag, txd->rndis_pkt,
541815516c77SSepherosa Ziehau 	    txd->rndis_pkt_dmap);
541915516c77SSepherosa Ziehau 	bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap);
542015516c77SSepherosa Ziehau }
542115516c77SSepherosa Ziehau 
542215516c77SSepherosa Ziehau static void
542325641fc7SSepherosa Ziehau hn_txdesc_gc(struct hn_tx_ring *txr, struct hn_txdesc *txd)
542425641fc7SSepherosa Ziehau {
542525641fc7SSepherosa Ziehau 
542625641fc7SSepherosa Ziehau 	KASSERT(txd->refs == 0 || txd->refs == 1,
542725641fc7SSepherosa Ziehau 	    ("invalid txd refs %d", txd->refs));
542825641fc7SSepherosa Ziehau 
542925641fc7SSepherosa Ziehau 	/* Aggregated txds will be freed by their aggregating txd. */
543025641fc7SSepherosa Ziehau 	if (txd->refs > 0 && (txd->flags & HN_TXD_FLAG_ONAGG) == 0) {
5431a0f49d67SMateusz Guzik 		int freed __diagused;
543225641fc7SSepherosa Ziehau 
543325641fc7SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
543425641fc7SSepherosa Ziehau 		KASSERT(freed, ("can't free txdesc"));
543525641fc7SSepherosa Ziehau 	}
543625641fc7SSepherosa Ziehau }
543725641fc7SSepherosa Ziehau 
543825641fc7SSepherosa Ziehau static void
543915516c77SSepherosa Ziehau hn_tx_ring_destroy(struct hn_tx_ring *txr)
544015516c77SSepherosa Ziehau {
544125641fc7SSepherosa Ziehau 	int i;
544215516c77SSepherosa Ziehau 
544315516c77SSepherosa Ziehau 	if (txr->hn_txdesc == NULL)
544415516c77SSepherosa Ziehau 		return;
544515516c77SSepherosa Ziehau 
544625641fc7SSepherosa Ziehau 	/*
544725641fc7SSepherosa Ziehau 	 * NOTE:
544825641fc7SSepherosa Ziehau 	 * Because the freeing of aggregated txds will be deferred
544925641fc7SSepherosa Ziehau 	 * to the aggregating txd, two passes are used here:
545025641fc7SSepherosa Ziehau 	 * - The first pass GCes any pending txds.  This GC is necessary,
545125641fc7SSepherosa Ziehau 	 *   since if the channels are revoked, hypervisor will not
545225641fc7SSepherosa Ziehau 	 *   deliver send-done for all pending txds.
545325641fc7SSepherosa Ziehau 	 * - The second pass frees the busdma stuffs, i.e. after all txds
545425641fc7SSepherosa Ziehau 	 *   were freed.
545525641fc7SSepherosa Ziehau 	 */
545625641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
545725641fc7SSepherosa Ziehau 		hn_txdesc_gc(txr, &txr->hn_txdesc[i]);
545825641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
545925641fc7SSepherosa Ziehau 		hn_txdesc_dmamap_destroy(&txr->hn_txdesc[i]);
546015516c77SSepherosa Ziehau 
546115516c77SSepherosa Ziehau 	if (txr->hn_tx_data_dtag != NULL)
546215516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_data_dtag);
546315516c77SSepherosa Ziehau 	if (txr->hn_tx_rndis_dtag != NULL)
546415516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_rndis_dtag);
546515516c77SSepherosa Ziehau 
546615516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
546715516c77SSepherosa Ziehau 	buf_ring_free(txr->hn_txdesc_br, M_DEVBUF);
546815516c77SSepherosa Ziehau #endif
546915516c77SSepherosa Ziehau 
547015516c77SSepherosa Ziehau 	free(txr->hn_txdesc, M_DEVBUF);
547115516c77SSepherosa Ziehau 	txr->hn_txdesc = NULL;
547215516c77SSepherosa Ziehau 
547315516c77SSepherosa Ziehau 	if (txr->hn_mbuf_br != NULL)
547415516c77SSepherosa Ziehau 		buf_ring_free(txr->hn_mbuf_br, M_DEVBUF);
547515516c77SSepherosa Ziehau 
547615516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
547715516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_txlist_spin);
547815516c77SSepherosa Ziehau #endif
547915516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_tx_lock);
548015516c77SSepherosa Ziehau }
548115516c77SSepherosa Ziehau 
548215516c77SSepherosa Ziehau static int
548315516c77SSepherosa Ziehau hn_create_tx_data(struct hn_softc *sc, int ring_cnt)
548415516c77SSepherosa Ziehau {
548515516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
548615516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
548715516c77SSepherosa Ziehau 	int i;
548815516c77SSepherosa Ziehau 
548915516c77SSepherosa Ziehau 	/*
549015516c77SSepherosa Ziehau 	 * Create TXBUF for chimney sending.
549115516c77SSepherosa Ziehau 	 *
549215516c77SSepherosa Ziehau 	 * NOTE: It is shared by all channels.
549315516c77SSepherosa Ziehau 	 */
549415516c77SSepherosa Ziehau 	sc->hn_chim = hyperv_dmamem_alloc(bus_get_dma_tag(sc->hn_dev),
549515516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_CHIM_SIZE, &sc->hn_chim_dma,
549615516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
549715516c77SSepherosa Ziehau 	if (sc->hn_chim == NULL) {
549815516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate txbuf failed\n");
549915516c77SSepherosa Ziehau 		return (ENOMEM);
550015516c77SSepherosa Ziehau 	}
550115516c77SSepherosa Ziehau 
550215516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = ring_cnt;
550315516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
550415516c77SSepherosa Ziehau 
550515516c77SSepherosa Ziehau 	sc->hn_tx_ring = malloc(sizeof(struct hn_tx_ring) * sc->hn_tx_ring_cnt,
550615516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
550715516c77SSepherosa Ziehau 
550815516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(sc->hn_dev);
550915516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->hn_dev));
551015516c77SSepherosa Ziehau 
551115516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.tx sysctl tree */
551215516c77SSepherosa Ziehau 	sc->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tx",
551315516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
551415516c77SSepherosa Ziehau 
551515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
551615516c77SSepherosa Ziehau 		int error;
551715516c77SSepherosa Ziehau 
551815516c77SSepherosa Ziehau 		error = hn_tx_ring_create(sc, i);
551915516c77SSepherosa Ziehau 		if (error)
552015516c77SSepherosa Ziehau 			return error;
552115516c77SSepherosa Ziehau 	}
552215516c77SSepherosa Ziehau 
552315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "no_txdescs",
5524db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
552515516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_no_txdescs),
552615516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of times short of TX descs");
552715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "send_failed",
5528db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
552915516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_send_failed),
553015516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of hyper-v sending failure");
553115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdma_failed",
5532db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
553315516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_txdma_failed),
553415516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX DMA failure");
5535dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_flush_failed",
5536db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
5537dc13fee6SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_flush_failed),
5538dc13fee6SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU",
5539dc13fee6SSepherosa Ziehau 	    "# of packet transmission aggregation flush failure");
554015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_collapsed",
5541db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
554215516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_collapsed),
554315516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX mbuf collapsed");
554415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney",
5545db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
554615516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney),
554715516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send");
554815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_tried",
5549db0ac6deSCy Schubert 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE | CTLFLAG_STATS, sc,
555015516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney_tried),
555115516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send tries");
555215516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_cnt",
555315516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_txdesc_cnt, 0,
555415516c77SSepherosa Ziehau 	    "# of total TX descs");
555515516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_chimney_max",
555615516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_chim_szmax, 0,
555715516c77SSepherosa Ziehau 	    "Chimney send packet size upper boundary");
555815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_size",
555915516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
556015516c77SSepherosa Ziehau 	    hn_chim_size_sysctl, "I", "Chimney send packet size limit");
556115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "direct_tx_size",
556215516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
556315516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_direct_tx_size),
556415516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
556515516c77SSepherosa Ziehau 	    "Size of the packet for direct transmission");
556615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "sched_tx",
556715516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
556815516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_sched_tx),
556915516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
557015516c77SSepherosa Ziehau 	    "Always schedule transmission "
557115516c77SSepherosa Ziehau 	    "instead of doing direct transmission");
557215516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_cnt",
557315516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_cnt, 0, "# created TX rings");
557415516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_inuse",
557515516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_inuse, 0, "# used TX rings");
5576dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "agg_szmax",
5577dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_agg_szmax, 0,
5578dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation size");
5579dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pktmax",
5580dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5581dc13fee6SSepherosa Ziehau 	    hn_txagg_pktmax_sysctl, "I",
5582dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation packets");
5583dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_align",
5584dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5585dc13fee6SSepherosa Ziehau 	    hn_txagg_align_sysctl, "I",
5586dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation alignment");
558715516c77SSepherosa Ziehau 
558815516c77SSepherosa Ziehau 	return 0;
558915516c77SSepherosa Ziehau }
559015516c77SSepherosa Ziehau 
559115516c77SSepherosa Ziehau static void
559215516c77SSepherosa Ziehau hn_set_chim_size(struct hn_softc *sc, int chim_size)
559315516c77SSepherosa Ziehau {
559415516c77SSepherosa Ziehau 	int i;
559515516c77SSepherosa Ziehau 
5596a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
559715516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_chim_size = chim_size;
559815516c77SSepherosa Ziehau }
559915516c77SSepherosa Ziehau 
560015516c77SSepherosa Ziehau static void
560115516c77SSepherosa Ziehau hn_set_tso_maxsize(struct hn_softc *sc, int tso_maxlen, int mtu)
560215516c77SSepherosa Ziehau {
5603*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
56049c6cae24SSepherosa Ziehau 	u_int hw_tsomax;
560515516c77SSepherosa Ziehau 	int tso_minlen;
560615516c77SSepherosa Ziehau 
56079c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
56089c6cae24SSepherosa Ziehau 
5609*4db5958aSJustin Hibbits 	if ((if_getcapabilities(ifp) & (IFCAP_TSO4 | IFCAP_TSO6)) == 0)
561015516c77SSepherosa Ziehau 		return;
561115516c77SSepherosa Ziehau 
561215516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_sgmin >= 2,
561315516c77SSepherosa Ziehau 	    ("invalid NDIS tso sgmin %d", sc->hn_ndis_tso_sgmin));
561415516c77SSepherosa Ziehau 	tso_minlen = sc->hn_ndis_tso_sgmin * mtu;
561515516c77SSepherosa Ziehau 
561615516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_szmax >= tso_minlen &&
561715516c77SSepherosa Ziehau 	    sc->hn_ndis_tso_szmax <= IP_MAXPACKET,
561815516c77SSepherosa Ziehau 	    ("invalid NDIS tso szmax %d", sc->hn_ndis_tso_szmax));
561915516c77SSepherosa Ziehau 
562015516c77SSepherosa Ziehau 	if (tso_maxlen < tso_minlen)
562115516c77SSepherosa Ziehau 		tso_maxlen = tso_minlen;
562215516c77SSepherosa Ziehau 	else if (tso_maxlen > IP_MAXPACKET)
562315516c77SSepherosa Ziehau 		tso_maxlen = IP_MAXPACKET;
562415516c77SSepherosa Ziehau 	if (tso_maxlen > sc->hn_ndis_tso_szmax)
562515516c77SSepherosa Ziehau 		tso_maxlen = sc->hn_ndis_tso_szmax;
56269c6cae24SSepherosa Ziehau 	hw_tsomax = tso_maxlen - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
56279c6cae24SSepherosa Ziehau 
56289c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
5629*4db5958aSJustin Hibbits 		if (hw_tsomax > if_gethwtsomax(sc->hn_vf_ifp))
5630*4db5958aSJustin Hibbits 			hw_tsomax = if_gethwtsomax(sc->hn_vf_ifp);
56319c6cae24SSepherosa Ziehau 	}
5632*4db5958aSJustin Hibbits 	if_sethwtsomax(ifp, hw_tsomax);
563315516c77SSepherosa Ziehau 	if (bootverbose)
5634*4db5958aSJustin Hibbits 		if_printf(ifp, "TSO size max %u\n", if_gethwtsomax(ifp));
563515516c77SSepherosa Ziehau }
563615516c77SSepherosa Ziehau 
563715516c77SSepherosa Ziehau static void
563815516c77SSepherosa Ziehau hn_fixup_tx_data(struct hn_softc *sc)
563915516c77SSepherosa Ziehau {
564015516c77SSepherosa Ziehau 	uint64_t csum_assist;
564115516c77SSepherosa Ziehau 	int i;
564215516c77SSepherosa Ziehau 
564315516c77SSepherosa Ziehau 	hn_set_chim_size(sc, sc->hn_chim_szmax);
564415516c77SSepherosa Ziehau 	if (hn_tx_chimney_size > 0 &&
564515516c77SSepherosa Ziehau 	    hn_tx_chimney_size < sc->hn_chim_szmax)
564615516c77SSepherosa Ziehau 		hn_set_chim_size(sc, hn_tx_chimney_size);
564715516c77SSepherosa Ziehau 
564815516c77SSepherosa Ziehau 	csum_assist = 0;
564915516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_IPCS)
565015516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP;
565115516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP4CS)
565215516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_TCP;
56532be266caSSepherosa Ziehau 	if ((sc->hn_caps & HN_CAP_UDP4CS) && hn_enable_udp4cs)
565415516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_UDP;
565515516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP6CS)
565615516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_TCP;
56572be266caSSepherosa Ziehau 	if ((sc->hn_caps & HN_CAP_UDP6CS) && hn_enable_udp6cs)
565815516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_UDP;
565915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
566015516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_csum_assist = csum_assist;
566115516c77SSepherosa Ziehau 
566215516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_HASHVAL) {
566315516c77SSepherosa Ziehau 		/*
566415516c77SSepherosa Ziehau 		 * Support HASHVAL pktinfo on TX path.
566515516c77SSepherosa Ziehau 		 */
566615516c77SSepherosa Ziehau 		if (bootverbose)
566715516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "support HASHVAL pktinfo\n");
566815516c77SSepherosa Ziehau 		for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
566915516c77SSepherosa Ziehau 			sc->hn_tx_ring[i].hn_tx_flags |= HN_TX_FLAG_HASHVAL;
567015516c77SSepherosa Ziehau 	}
567115516c77SSepherosa Ziehau }
567215516c77SSepherosa Ziehau 
567315516c77SSepherosa Ziehau static void
5674db76829bSSepherosa Ziehau hn_fixup_rx_data(struct hn_softc *sc)
5675db76829bSSepherosa Ziehau {
5676db76829bSSepherosa Ziehau 
5677db76829bSSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_UDPHASH) {
5678db76829bSSepherosa Ziehau 		int i;
5679db76829bSSepherosa Ziehau 
5680db76829bSSepherosa Ziehau 		for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
5681db76829bSSepherosa Ziehau 			sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_UDP_HASH;
5682db76829bSSepherosa Ziehau 	}
5683db76829bSSepherosa Ziehau }
5684db76829bSSepherosa Ziehau 
5685db76829bSSepherosa Ziehau static void
568615516c77SSepherosa Ziehau hn_destroy_tx_data(struct hn_softc *sc)
568715516c77SSepherosa Ziehau {
568815516c77SSepherosa Ziehau 	int i;
568915516c77SSepherosa Ziehau 
569015516c77SSepherosa Ziehau 	if (sc->hn_chim != NULL) {
56912494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_CHIM_REF) == 0) {
569215516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_chim_dma, sc->hn_chim);
56932494d735SSepherosa Ziehau 		} else {
56942494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
56952494d735SSepherosa Ziehau 			    "chimney sending buffer is referenced");
56962494d735SSepherosa Ziehau 		}
569715516c77SSepherosa Ziehau 		sc->hn_chim = NULL;
569815516c77SSepherosa Ziehau 	}
569915516c77SSepherosa Ziehau 
570015516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt == 0)
570115516c77SSepherosa Ziehau 		return;
570215516c77SSepherosa Ziehau 
570315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
570415516c77SSepherosa Ziehau 		hn_tx_ring_destroy(&sc->hn_tx_ring[i]);
570515516c77SSepherosa Ziehau 
570615516c77SSepherosa Ziehau 	free(sc->hn_tx_ring, M_DEVBUF);
570715516c77SSepherosa Ziehau 	sc->hn_tx_ring = NULL;
570815516c77SSepherosa Ziehau 
570915516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = 0;
571015516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = 0;
571115516c77SSepherosa Ziehau }
571215516c77SSepherosa Ziehau 
571323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
571423bf9e15SSepherosa Ziehau 
571515516c77SSepherosa Ziehau static void
571615516c77SSepherosa Ziehau hn_start_taskfunc(void *xtxr, int pending __unused)
571715516c77SSepherosa Ziehau {
571815516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
571915516c77SSepherosa Ziehau 
572015516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
572115516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
572215516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
572315516c77SSepherosa Ziehau }
572415516c77SSepherosa Ziehau 
572523bf9e15SSepherosa Ziehau static int
572623bf9e15SSepherosa Ziehau hn_start_locked(struct hn_tx_ring *txr, int len)
572723bf9e15SSepherosa Ziehau {
572823bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
5729*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
5730dc13fee6SSepherosa Ziehau 	int sched = 0;
573123bf9e15SSepherosa Ziehau 
573223bf9e15SSepherosa Ziehau 	KASSERT(hn_use_if_start,
573323bf9e15SSepherosa Ziehau 	    ("hn_start_locked is called, when if_start is disabled"));
573423bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
573523bf9e15SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
5736dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
573723bf9e15SSepherosa Ziehau 
573823bf9e15SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5739dc13fee6SSepherosa Ziehau 		return (0);
574023bf9e15SSepherosa Ziehau 
5741*4db5958aSJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
574223bf9e15SSepherosa Ziehau 	    IFF_DRV_RUNNING)
5743dc13fee6SSepherosa Ziehau 		return (0);
574423bf9e15SSepherosa Ziehau 
5745*4db5958aSJustin Hibbits 	while (!if_sendq_empty(ifp)) {
574623bf9e15SSepherosa Ziehau 		struct hn_txdesc *txd;
574723bf9e15SSepherosa Ziehau 		struct mbuf *m_head;
574823bf9e15SSepherosa Ziehau 		int error;
574923bf9e15SSepherosa Ziehau 
5750*4db5958aSJustin Hibbits 		m_head = if_dequeue(ifp);
575123bf9e15SSepherosa Ziehau 		if (m_head == NULL)
575223bf9e15SSepherosa Ziehau 			break;
575323bf9e15SSepherosa Ziehau 
575423bf9e15SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
575523bf9e15SSepherosa Ziehau 			/*
575623bf9e15SSepherosa Ziehau 			 * This sending could be time consuming; let callers
575723bf9e15SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
575823bf9e15SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
575923bf9e15SSepherosa Ziehau 			 */
5760*4db5958aSJustin Hibbits 			if_sendq_prepend(ifp, m_head);
5761dc13fee6SSepherosa Ziehau 			sched = 1;
5762dc13fee6SSepherosa Ziehau 			break;
576323bf9e15SSepherosa Ziehau 		}
576423bf9e15SSepherosa Ziehau 
5765edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
5766edd3f315SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
5767edd3f315SSepherosa Ziehau 			m_head = hn_tso_fixup(m_head);
5768edd3f315SSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5769edd3f315SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5770edd3f315SSepherosa Ziehau 				continue;
5771edd3f315SSepherosa Ziehau 			}
5772c49d47daSSepherosa Ziehau 		} else if (m_head->m_pkthdr.csum_flags &
5773c49d47daSSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP)) {
5774c49d47daSSepherosa Ziehau 			m_head = hn_set_hlen(m_head);
5775c49d47daSSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5776c49d47daSSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5777c49d47daSSepherosa Ziehau 				continue;
5778c49d47daSSepherosa Ziehau 			}
5779edd3f315SSepherosa Ziehau 		}
5780edd3f315SSepherosa Ziehau #endif
5781edd3f315SSepherosa Ziehau 
578223bf9e15SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
578323bf9e15SSepherosa Ziehau 		if (txd == NULL) {
578423bf9e15SSepherosa Ziehau 			txr->hn_no_txdescs++;
5785*4db5958aSJustin Hibbits 			if_sendq_prepend(ifp, m_head);
5786*4db5958aSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
578723bf9e15SSepherosa Ziehau 			break;
578823bf9e15SSepherosa Ziehau 		}
578923bf9e15SSepherosa Ziehau 
5790dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
579123bf9e15SSepherosa Ziehau 		if (error) {
579223bf9e15SSepherosa Ziehau 			/* Both txd and m_head are freed */
5793dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5794dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
579523bf9e15SSepherosa Ziehau 			continue;
579623bf9e15SSepherosa Ziehau 		}
579723bf9e15SSepherosa Ziehau 
5798dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5799dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5800dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5801dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5802dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
5803dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5804*4db5958aSJustin Hibbits 					if_setdrvflagbits(ifp,
5805*4db5958aSJustin Hibbits 					    IFF_DRV_OACTIVE, 0);
5806dc13fee6SSepherosa Ziehau 					break;
5807dc13fee6SSepherosa Ziehau 				}
5808dc13fee6SSepherosa Ziehau 			} else {
5809dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
581023bf9e15SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
581123bf9e15SSepherosa Ziehau 				if (__predict_false(error)) {
581223bf9e15SSepherosa Ziehau 					/* txd is freed, but m_head is not */
5813*4db5958aSJustin Hibbits 					if_sendq_prepend(ifp, m_head);
5814*4db5958aSJustin Hibbits 					if_setdrvflagbits(ifp,
5815*4db5958aSJustin Hibbits 					    IFF_DRV_OACTIVE, 0);
581623bf9e15SSepherosa Ziehau 					break;
581723bf9e15SSepherosa Ziehau 				}
581823bf9e15SSepherosa Ziehau 			}
5819dc13fee6SSepherosa Ziehau 		}
5820dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5821dc13fee6SSepherosa Ziehau 		else {
5822dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5823dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5824dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5825dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5826dc13fee6SSepherosa Ziehau 		}
5827dc13fee6SSepherosa Ziehau #endif
5828dc13fee6SSepherosa Ziehau 	}
5829dc13fee6SSepherosa Ziehau 
5830dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5831dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5832dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5833dc13fee6SSepherosa Ziehau 	return (sched);
583423bf9e15SSepherosa Ziehau }
583523bf9e15SSepherosa Ziehau 
583623bf9e15SSepherosa Ziehau static void
5837*4db5958aSJustin Hibbits hn_start(if_t ifp)
583823bf9e15SSepherosa Ziehau {
5839*4db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
584023bf9e15SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[0];
584123bf9e15SSepherosa Ziehau 
584223bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
584323bf9e15SSepherosa Ziehau 		goto do_sched;
584423bf9e15SSepherosa Ziehau 
584523bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
584623bf9e15SSepherosa Ziehau 		int sched;
584723bf9e15SSepherosa Ziehau 
584823bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
584923bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
585023bf9e15SSepherosa Ziehau 		if (!sched)
585123bf9e15SSepherosa Ziehau 			return;
585223bf9e15SSepherosa Ziehau 	}
585323bf9e15SSepherosa Ziehau do_sched:
585423bf9e15SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
585523bf9e15SSepherosa Ziehau }
585623bf9e15SSepherosa Ziehau 
585715516c77SSepherosa Ziehau static void
585815516c77SSepherosa Ziehau hn_start_txeof_taskfunc(void *xtxr, int pending __unused)
585915516c77SSepherosa Ziehau {
586015516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
586115516c77SSepherosa Ziehau 
586215516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
5863*4db5958aSJustin Hibbits 	if_setdrvflagbits(txr->hn_sc->hn_ifp, 0, IFF_DRV_OACTIVE);
586415516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
586515516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
586615516c77SSepherosa Ziehau }
586715516c77SSepherosa Ziehau 
586823bf9e15SSepherosa Ziehau static void
586923bf9e15SSepherosa Ziehau hn_start_txeof(struct hn_tx_ring *txr)
587023bf9e15SSepherosa Ziehau {
587123bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
5872*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
587323bf9e15SSepherosa Ziehau 
587423bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
587523bf9e15SSepherosa Ziehau 
587623bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
587723bf9e15SSepherosa Ziehau 		goto do_sched;
587823bf9e15SSepherosa Ziehau 
587923bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
588023bf9e15SSepherosa Ziehau 		int sched;
588123bf9e15SSepherosa Ziehau 
5882*4db5958aSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
588323bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
588423bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
588523bf9e15SSepherosa Ziehau 		if (sched) {
588623bf9e15SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
588723bf9e15SSepherosa Ziehau 			    &txr->hn_tx_task);
588823bf9e15SSepherosa Ziehau 		}
588923bf9e15SSepherosa Ziehau 	} else {
589023bf9e15SSepherosa Ziehau do_sched:
589123bf9e15SSepherosa Ziehau 		/*
589223bf9e15SSepherosa Ziehau 		 * Release the OACTIVE earlier, with the hope, that
589323bf9e15SSepherosa Ziehau 		 * others could catch up.  The task will clear the
589423bf9e15SSepherosa Ziehau 		 * flag again with the hn_tx_lock to avoid possible
589523bf9e15SSepherosa Ziehau 		 * races.
589623bf9e15SSepherosa Ziehau 		 */
5897*4db5958aSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
589823bf9e15SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
589923bf9e15SSepherosa Ziehau 	}
590023bf9e15SSepherosa Ziehau }
590123bf9e15SSepherosa Ziehau 
590223bf9e15SSepherosa Ziehau #endif	/* HN_IFSTART_SUPPORT */
590323bf9e15SSepherosa Ziehau 
590415516c77SSepherosa Ziehau static int
590515516c77SSepherosa Ziehau hn_xmit(struct hn_tx_ring *txr, int len)
590615516c77SSepherosa Ziehau {
590715516c77SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
5908*4db5958aSJustin Hibbits 	if_t ifp = sc->hn_ifp;
590915516c77SSepherosa Ziehau 	struct mbuf *m_head;
5910dc13fee6SSepherosa Ziehau 	int sched = 0;
591115516c77SSepherosa Ziehau 
591215516c77SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
591323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
591415516c77SSepherosa Ziehau 	KASSERT(hn_use_if_start == 0,
591515516c77SSepherosa Ziehau 	    ("hn_xmit is called, when if_start is enabled"));
591623bf9e15SSepherosa Ziehau #endif
5917dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
591815516c77SSepherosa Ziehau 
591915516c77SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5920dc13fee6SSepherosa Ziehau 		return (0);
592115516c77SSepherosa Ziehau 
5922*4db5958aSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0 || txr->hn_oactive)
5923dc13fee6SSepherosa Ziehau 		return (0);
592415516c77SSepherosa Ziehau 
592515516c77SSepherosa Ziehau 	while ((m_head = drbr_peek(ifp, txr->hn_mbuf_br)) != NULL) {
592615516c77SSepherosa Ziehau 		struct hn_txdesc *txd;
592715516c77SSepherosa Ziehau 		int error;
592815516c77SSepherosa Ziehau 
592915516c77SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
593015516c77SSepherosa Ziehau 			/*
593115516c77SSepherosa Ziehau 			 * This sending could be time consuming; let callers
593215516c77SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
593315516c77SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
593415516c77SSepherosa Ziehau 			 */
593515516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
5936dc13fee6SSepherosa Ziehau 			sched = 1;
5937dc13fee6SSepherosa Ziehau 			break;
593815516c77SSepherosa Ziehau 		}
593915516c77SSepherosa Ziehau 
594015516c77SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
594115516c77SSepherosa Ziehau 		if (txd == NULL) {
594215516c77SSepherosa Ziehau 			txr->hn_no_txdescs++;
594315516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
594415516c77SSepherosa Ziehau 			txr->hn_oactive = 1;
594515516c77SSepherosa Ziehau 			break;
594615516c77SSepherosa Ziehau 		}
594715516c77SSepherosa Ziehau 
5948dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
594915516c77SSepherosa Ziehau 		if (error) {
595015516c77SSepherosa Ziehau 			/* Both txd and m_head are freed; discard */
5951dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5952dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
595315516c77SSepherosa Ziehau 			drbr_advance(ifp, txr->hn_mbuf_br);
595415516c77SSepherosa Ziehau 			continue;
595515516c77SSepherosa Ziehau 		}
595615516c77SSepherosa Ziehau 
5957dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5958dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5959dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5960dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5961dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
596215516c77SSepherosa Ziehau 				if (__predict_false(error)) {
596315516c77SSepherosa Ziehau 					txr->hn_oactive = 1;
596415516c77SSepherosa Ziehau 					break;
596515516c77SSepherosa Ziehau 				}
5966dc13fee6SSepherosa Ziehau 			} else {
5967dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
5968dc13fee6SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
5969dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5970dc13fee6SSepherosa Ziehau 					/* txd is freed, but m_head is not */
5971dc13fee6SSepherosa Ziehau 					drbr_putback(ifp, txr->hn_mbuf_br,
5972dc13fee6SSepherosa Ziehau 					    m_head);
5973dc13fee6SSepherosa Ziehau 					txr->hn_oactive = 1;
5974dc13fee6SSepherosa Ziehau 					break;
5975dc13fee6SSepherosa Ziehau 				}
5976dc13fee6SSepherosa Ziehau 			}
5977dc13fee6SSepherosa Ziehau 		}
5978dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5979dc13fee6SSepherosa Ziehau 		else {
5980dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5981dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5982dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5983dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5984dc13fee6SSepherosa Ziehau 		}
5985dc13fee6SSepherosa Ziehau #endif
598615516c77SSepherosa Ziehau 
598715516c77SSepherosa Ziehau 		/* Sent */
598815516c77SSepherosa Ziehau 		drbr_advance(ifp, txr->hn_mbuf_br);
598915516c77SSepherosa Ziehau 	}
5990dc13fee6SSepherosa Ziehau 
5991dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5992dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5993dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5994dc13fee6SSepherosa Ziehau 	return (sched);
599515516c77SSepherosa Ziehau }
599615516c77SSepherosa Ziehau 
599715516c77SSepherosa Ziehau static int
5998*4db5958aSJustin Hibbits hn_transmit(if_t ifp, struct mbuf *m)
599915516c77SSepherosa Ziehau {
6000*4db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
600115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
600215516c77SSepherosa Ziehau 	int error, idx = 0;
600315516c77SSepherosa Ziehau 
60049c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
60059c6cae24SSepherosa Ziehau 		struct rm_priotracker pt;
60069c6cae24SSepherosa Ziehau 
60079c6cae24SSepherosa Ziehau 		rm_rlock(&sc->hn_vf_lock, &pt);
60089c6cae24SSepherosa Ziehau 		if (__predict_true(sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
60099c6cae24SSepherosa Ziehau 			struct mbuf *m_bpf = NULL;
60109c6cae24SSepherosa Ziehau 			int obytes, omcast;
60119c6cae24SSepherosa Ziehau 
60129c6cae24SSepherosa Ziehau 			obytes = m->m_pkthdr.len;
60137898a1f4SEric van Gyzen 			omcast = (m->m_flags & M_MCAST) != 0;
60149c6cae24SSepherosa Ziehau 
60159c6cae24SSepherosa Ziehau 			if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF) {
6016*4db5958aSJustin Hibbits 				if (bpf_peers_present(if_getbpf(ifp))) {
60179c6cae24SSepherosa Ziehau 					m_bpf = m_copypacket(m, M_NOWAIT);
60189c6cae24SSepherosa Ziehau 					if (m_bpf == NULL) {
60199c6cae24SSepherosa Ziehau 						/*
60209c6cae24SSepherosa Ziehau 						 * Failed to grab a shallow
60219c6cae24SSepherosa Ziehau 						 * copy; tap now.
60229c6cae24SSepherosa Ziehau 						 */
60239c6cae24SSepherosa Ziehau 						ETHER_BPF_MTAP(ifp, m);
60249c6cae24SSepherosa Ziehau 					}
60259c6cae24SSepherosa Ziehau 				}
60269c6cae24SSepherosa Ziehau 			} else {
60279c6cae24SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, m);
60289c6cae24SSepherosa Ziehau 			}
60299c6cae24SSepherosa Ziehau 
6030*4db5958aSJustin Hibbits 			error = if_transmit(sc->hn_vf_ifp, m);
60319c6cae24SSepherosa Ziehau 			rm_runlock(&sc->hn_vf_lock, &pt);
60329c6cae24SSepherosa Ziehau 
60339c6cae24SSepherosa Ziehau 			if (m_bpf != NULL) {
60349c6cae24SSepherosa Ziehau 				if (!error)
60359c6cae24SSepherosa Ziehau 					ETHER_BPF_MTAP(ifp, m_bpf);
60369c6cae24SSepherosa Ziehau 				m_freem(m_bpf);
60379c6cae24SSepherosa Ziehau 			}
60389c6cae24SSepherosa Ziehau 
60399c6cae24SSepherosa Ziehau 			if (error == ENOBUFS) {
60409c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
60419c6cae24SSepherosa Ziehau 			} else if (error) {
60429c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
60439c6cae24SSepherosa Ziehau 			} else {
60449c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
60459c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OBYTES, obytes);
60469c6cae24SSepherosa Ziehau 				if (omcast) {
60479c6cae24SSepherosa Ziehau 					if_inc_counter(ifp, IFCOUNTER_OMCASTS,
60489c6cae24SSepherosa Ziehau 					    omcast);
60499c6cae24SSepherosa Ziehau 				}
60509c6cae24SSepherosa Ziehau 			}
60519c6cae24SSepherosa Ziehau 			return (error);
60529c6cae24SSepherosa Ziehau 		}
60539c6cae24SSepherosa Ziehau 		rm_runlock(&sc->hn_vf_lock, &pt);
60549c6cae24SSepherosa Ziehau 	}
60559c6cae24SSepherosa Ziehau 
6056edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
6057edd3f315SSepherosa Ziehau 	/*
6058c49d47daSSepherosa Ziehau 	 * Perform TSO packet header fixup or get l2/l3 header length now,
6059c49d47daSSepherosa Ziehau 	 * since packet headers should be cache-hot.
6060edd3f315SSepherosa Ziehau 	 */
6061edd3f315SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
6062edd3f315SSepherosa Ziehau 		m = hn_tso_fixup(m);
6063edd3f315SSepherosa Ziehau 		if (__predict_false(m == NULL)) {
6064edd3f315SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
6065edd3f315SSepherosa Ziehau 			return EIO;
6066edd3f315SSepherosa Ziehau 		}
6067c49d47daSSepherosa Ziehau 	} else if (m->m_pkthdr.csum_flags &
6068c49d47daSSepherosa Ziehau 	    (CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP)) {
6069c49d47daSSepherosa Ziehau 		m = hn_set_hlen(m);
6070c49d47daSSepherosa Ziehau 		if (__predict_false(m == NULL)) {
6071c49d47daSSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
6072c49d47daSSepherosa Ziehau 			return EIO;
6073c49d47daSSepherosa Ziehau 		}
6074edd3f315SSepherosa Ziehau 	}
6075edd3f315SSepherosa Ziehau #endif
6076edd3f315SSepherosa Ziehau 
607715516c77SSepherosa Ziehau 	/*
607815516c77SSepherosa Ziehau 	 * Select the TX ring based on flowid
607915516c77SSepherosa Ziehau 	 */
608034d68912SSepherosa Ziehau 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
608134d68912SSepherosa Ziehau #ifdef RSS
608234d68912SSepherosa Ziehau 		uint32_t bid;
608334d68912SSepherosa Ziehau 
608434d68912SSepherosa Ziehau 		if (rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m),
608534d68912SSepherosa Ziehau 		    &bid) == 0)
608634d68912SSepherosa Ziehau 			idx = bid % sc->hn_tx_ring_inuse;
608734d68912SSepherosa Ziehau 		else
608834d68912SSepherosa Ziehau #endif
6089cc0c6ebcSSepherosa Ziehau 		{
6090cc0c6ebcSSepherosa Ziehau #if defined(INET6) || defined(INET)
6091cc0c6ebcSSepherosa Ziehau 			int tcpsyn = 0;
6092cc0c6ebcSSepherosa Ziehau 
6093cc0c6ebcSSepherosa Ziehau 			if (m->m_pkthdr.len < 128 &&
6094cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags &
6095cc0c6ebcSSepherosa Ziehau 			     (CSUM_IP_TCP | CSUM_IP6_TCP)) &&
6096cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
6097cc0c6ebcSSepherosa Ziehau 				m = hn_check_tcpsyn(m, &tcpsyn);
6098cc0c6ebcSSepherosa Ziehau 				if (__predict_false(m == NULL)) {
6099cc0c6ebcSSepherosa Ziehau 					if_inc_counter(ifp,
6100cc0c6ebcSSepherosa Ziehau 					    IFCOUNTER_OERRORS, 1);
6101cc0c6ebcSSepherosa Ziehau 					return (EIO);
6102cc0c6ebcSSepherosa Ziehau 				}
6103cc0c6ebcSSepherosa Ziehau 			}
6104cc0c6ebcSSepherosa Ziehau #else
6105cc0c6ebcSSepherosa Ziehau 			const int tcpsyn = 0;
6106cc0c6ebcSSepherosa Ziehau #endif
6107cc0c6ebcSSepherosa Ziehau 			if (tcpsyn)
6108cc0c6ebcSSepherosa Ziehau 				idx = 0;
6109cc0c6ebcSSepherosa Ziehau 			else
611015516c77SSepherosa Ziehau 				idx = m->m_pkthdr.flowid % sc->hn_tx_ring_inuse;
611134d68912SSepherosa Ziehau 		}
6112cc0c6ebcSSepherosa Ziehau 	}
611315516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[idx];
611415516c77SSepherosa Ziehau 
611515516c77SSepherosa Ziehau 	error = drbr_enqueue(ifp, txr->hn_mbuf_br, m);
611615516c77SSepherosa Ziehau 	if (error) {
611715516c77SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
611815516c77SSepherosa Ziehau 		return error;
611915516c77SSepherosa Ziehau 	}
612015516c77SSepherosa Ziehau 
612115516c77SSepherosa Ziehau 	if (txr->hn_oactive)
612215516c77SSepherosa Ziehau 		return 0;
612315516c77SSepherosa Ziehau 
612415516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
612515516c77SSepherosa Ziehau 		goto do_sched;
612615516c77SSepherosa Ziehau 
612715516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
612815516c77SSepherosa Ziehau 		int sched;
612915516c77SSepherosa Ziehau 
613015516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
613115516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
613215516c77SSepherosa Ziehau 		if (!sched)
613315516c77SSepherosa Ziehau 			return 0;
613415516c77SSepherosa Ziehau 	}
613515516c77SSepherosa Ziehau do_sched:
613615516c77SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
613715516c77SSepherosa Ziehau 	return 0;
613815516c77SSepherosa Ziehau }
613915516c77SSepherosa Ziehau 
614015516c77SSepherosa Ziehau static void
614115516c77SSepherosa Ziehau hn_tx_ring_qflush(struct hn_tx_ring *txr)
614215516c77SSepherosa Ziehau {
614315516c77SSepherosa Ziehau 	struct mbuf *m;
614415516c77SSepherosa Ziehau 
614515516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
614615516c77SSepherosa Ziehau 	while ((m = buf_ring_dequeue_sc(txr->hn_mbuf_br)) != NULL)
614715516c77SSepherosa Ziehau 		m_freem(m);
614815516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
614915516c77SSepherosa Ziehau }
615015516c77SSepherosa Ziehau 
615115516c77SSepherosa Ziehau static void
6152*4db5958aSJustin Hibbits hn_xmit_qflush(if_t ifp)
615315516c77SSepherosa Ziehau {
6154*4db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(ifp);
61559c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
615615516c77SSepherosa Ziehau 	int i;
615715516c77SSepherosa Ziehau 
615815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
615915516c77SSepherosa Ziehau 		hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
616015516c77SSepherosa Ziehau 	if_qflush(ifp);
61619c6cae24SSepherosa Ziehau 
61629c6cae24SSepherosa Ziehau 	rm_rlock(&sc->hn_vf_lock, &pt);
61639c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
6164*4db5958aSJustin Hibbits 		if_qflush(sc->hn_vf_ifp);
61659c6cae24SSepherosa Ziehau 	rm_runlock(&sc->hn_vf_lock, &pt);
616615516c77SSepherosa Ziehau }
616715516c77SSepherosa Ziehau 
616815516c77SSepherosa Ziehau static void
616915516c77SSepherosa Ziehau hn_xmit_txeof(struct hn_tx_ring *txr)
617015516c77SSepherosa Ziehau {
617115516c77SSepherosa Ziehau 
617215516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
617315516c77SSepherosa Ziehau 		goto do_sched;
617415516c77SSepherosa Ziehau 
617515516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
617615516c77SSepherosa Ziehau 		int sched;
617715516c77SSepherosa Ziehau 
617815516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
617915516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
618015516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
618115516c77SSepherosa Ziehau 		if (sched) {
618215516c77SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
618315516c77SSepherosa Ziehau 			    &txr->hn_tx_task);
618415516c77SSepherosa Ziehau 		}
618515516c77SSepherosa Ziehau 	} else {
618615516c77SSepherosa Ziehau do_sched:
618715516c77SSepherosa Ziehau 		/*
618815516c77SSepherosa Ziehau 		 * Release the oactive earlier, with the hope, that
618915516c77SSepherosa Ziehau 		 * others could catch up.  The task will clear the
619015516c77SSepherosa Ziehau 		 * oactive again with the hn_tx_lock to avoid possible
619115516c77SSepherosa Ziehau 		 * races.
619215516c77SSepherosa Ziehau 		 */
619315516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
619415516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
619515516c77SSepherosa Ziehau 	}
619615516c77SSepherosa Ziehau }
619715516c77SSepherosa Ziehau 
619815516c77SSepherosa Ziehau static void
619915516c77SSepherosa Ziehau hn_xmit_taskfunc(void *xtxr, int pending __unused)
620015516c77SSepherosa Ziehau {
620115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
620215516c77SSepherosa Ziehau 
620315516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
620415516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
620515516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
620615516c77SSepherosa Ziehau }
620715516c77SSepherosa Ziehau 
620815516c77SSepherosa Ziehau static void
620915516c77SSepherosa Ziehau hn_xmit_txeof_taskfunc(void *xtxr, int pending __unused)
621015516c77SSepherosa Ziehau {
621115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
621215516c77SSepherosa Ziehau 
621315516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
621415516c77SSepherosa Ziehau 	txr->hn_oactive = 0;
621515516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
621615516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
621715516c77SSepherosa Ziehau }
621815516c77SSepherosa Ziehau 
621915516c77SSepherosa Ziehau static int
622015516c77SSepherosa Ziehau hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
622115516c77SSepherosa Ziehau {
622215516c77SSepherosa Ziehau 	struct vmbus_chan_br cbr;
622315516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
622415516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = NULL;
622515516c77SSepherosa Ziehau 	int idx, error;
622615516c77SSepherosa Ziehau 
622715516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
622815516c77SSepherosa Ziehau 
622915516c77SSepherosa Ziehau 	/*
623015516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
623115516c77SSepherosa Ziehau 	 */
623215516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
623315516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
623415516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
623515516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
623615516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED) == 0,
623715516c77SSepherosa Ziehau 	    ("RX ring %d already attached", idx));
623815516c77SSepherosa Ziehau 	rxr->hn_rx_flags |= HN_RX_FLAG_ATTACHED;
62393ab0fea1SDexuan Cui 	rxr->hn_chan = chan;
624015516c77SSepherosa Ziehau 
624115516c77SSepherosa Ziehau 	if (bootverbose) {
624215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "link RX ring %d to chan%u\n",
624315516c77SSepherosa Ziehau 		    idx, vmbus_chan_id(chan));
624415516c77SSepherosa Ziehau 	}
624515516c77SSepherosa Ziehau 
624615516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
624715516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[idx];
624815516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED) == 0,
624915516c77SSepherosa Ziehau 		    ("TX ring %d already attached", idx));
625015516c77SSepherosa Ziehau 		txr->hn_tx_flags |= HN_TX_FLAG_ATTACHED;
625115516c77SSepherosa Ziehau 
625215516c77SSepherosa Ziehau 		txr->hn_chan = chan;
625315516c77SSepherosa Ziehau 		if (bootverbose) {
625415516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "link TX ring %d to chan%u\n",
625515516c77SSepherosa Ziehau 			    idx, vmbus_chan_id(chan));
625615516c77SSepherosa Ziehau 		}
625715516c77SSepherosa Ziehau 	}
625815516c77SSepherosa Ziehau 
625915516c77SSepherosa Ziehau 	/* Bind this channel to a proper CPU. */
62600e11868dSSepherosa Ziehau 	vmbus_chan_cpu_set(chan, HN_RING_IDX2CPU(sc, idx));
626115516c77SSepherosa Ziehau 
626215516c77SSepherosa Ziehau 	/*
626315516c77SSepherosa Ziehau 	 * Open this channel
626415516c77SSepherosa Ziehau 	 */
626515516c77SSepherosa Ziehau 	cbr.cbr = rxr->hn_br;
626615516c77SSepherosa Ziehau 	cbr.cbr_paddr = rxr->hn_br_dma.hv_paddr;
626715516c77SSepherosa Ziehau 	cbr.cbr_txsz = HN_TXBR_SIZE;
626815516c77SSepherosa Ziehau 	cbr.cbr_rxsz = HN_RXBR_SIZE;
626915516c77SSepherosa Ziehau 	error = vmbus_chan_open_br(chan, &cbr, NULL, 0, hn_chan_callback, rxr);
627015516c77SSepherosa Ziehau 	if (error) {
627171e8ac56SSepherosa Ziehau 		if (error == EISCONN) {
627271e8ac56SSepherosa Ziehau 			if_printf(sc->hn_ifp, "bufring is connected after "
627371e8ac56SSepherosa Ziehau 			    "chan%u open failure\n", vmbus_chan_id(chan));
627471e8ac56SSepherosa Ziehau 			rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
627571e8ac56SSepherosa Ziehau 		} else {
627615516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "open chan%u failed: %d\n",
627715516c77SSepherosa Ziehau 			    vmbus_chan_id(chan), error);
627871e8ac56SSepherosa Ziehau 		}
627915516c77SSepherosa Ziehau 	}
628015516c77SSepherosa Ziehau 	return (error);
628115516c77SSepherosa Ziehau }
628215516c77SSepherosa Ziehau 
628315516c77SSepherosa Ziehau static void
628415516c77SSepherosa Ziehau hn_chan_detach(struct hn_softc *sc, struct vmbus_channel *chan)
628515516c77SSepherosa Ziehau {
628615516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
62872494d735SSepherosa Ziehau 	int idx, error;
628815516c77SSepherosa Ziehau 
628915516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
629015516c77SSepherosa Ziehau 
629115516c77SSepherosa Ziehau 	/*
629215516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
629315516c77SSepherosa Ziehau 	 */
629415516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
629515516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
629615516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
629715516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
629815516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED),
629915516c77SSepherosa Ziehau 	    ("RX ring %d is not attached", idx));
630015516c77SSepherosa Ziehau 	rxr->hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
630115516c77SSepherosa Ziehau 
630215516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
630315516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[idx];
630415516c77SSepherosa Ziehau 
630515516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED),
630615516c77SSepherosa Ziehau 		    ("TX ring %d is not attached attached", idx));
630715516c77SSepherosa Ziehau 		txr->hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
630815516c77SSepherosa Ziehau 	}
630915516c77SSepherosa Ziehau 
631015516c77SSepherosa Ziehau 	/*
631115516c77SSepherosa Ziehau 	 * Close this channel.
631215516c77SSepherosa Ziehau 	 *
631315516c77SSepherosa Ziehau 	 * NOTE:
631415516c77SSepherosa Ziehau 	 * Channel closing does _not_ destroy the target channel.
631515516c77SSepherosa Ziehau 	 */
63162494d735SSepherosa Ziehau 	error = vmbus_chan_close_direct(chan);
63172494d735SSepherosa Ziehau 	if (error == EISCONN) {
6318aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u bufring is connected "
6319aa1a2adcSSepherosa Ziehau 		    "after being closed\n", vmbus_chan_id(chan));
63202494d735SSepherosa Ziehau 		rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
63212494d735SSepherosa Ziehau 	} else if (error) {
6322aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u close failed: %d\n",
6323aa1a2adcSSepherosa Ziehau 		    vmbus_chan_id(chan), error);
63242494d735SSepherosa Ziehau 	}
632515516c77SSepherosa Ziehau }
632615516c77SSepherosa Ziehau 
632715516c77SSepherosa Ziehau static int
632815516c77SSepherosa Ziehau hn_attach_subchans(struct hn_softc *sc)
632915516c77SSepherosa Ziehau {
633015516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
633115516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
633215516c77SSepherosa Ziehau 	int i, error = 0;
633315516c77SSepherosa Ziehau 
633471e8ac56SSepherosa Ziehau 	KASSERT(subchan_cnt > 0, ("no sub-channels"));
633515516c77SSepherosa Ziehau 
633615516c77SSepherosa Ziehau 	/* Attach the sub-channels. */
633715516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
633815516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i) {
633971e8ac56SSepherosa Ziehau 		int error1;
634071e8ac56SSepherosa Ziehau 
634171e8ac56SSepherosa Ziehau 		error1 = hn_chan_attach(sc, subchans[i]);
634271e8ac56SSepherosa Ziehau 		if (error1) {
634371e8ac56SSepherosa Ziehau 			error = error1;
634471e8ac56SSepherosa Ziehau 			/* Move on; all channels will be detached later. */
634571e8ac56SSepherosa Ziehau 		}
634615516c77SSepherosa Ziehau 	}
634715516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
634815516c77SSepherosa Ziehau 
634915516c77SSepherosa Ziehau 	if (error) {
635015516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "sub-channels attach failed: %d\n", error);
635115516c77SSepherosa Ziehau 	} else {
635215516c77SSepherosa Ziehau 		if (bootverbose) {
635315516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "%d sub-channels attached\n",
635415516c77SSepherosa Ziehau 			    subchan_cnt);
635515516c77SSepherosa Ziehau 		}
635615516c77SSepherosa Ziehau 	}
635715516c77SSepherosa Ziehau 	return (error);
635815516c77SSepherosa Ziehau }
635915516c77SSepherosa Ziehau 
636015516c77SSepherosa Ziehau static void
636115516c77SSepherosa Ziehau hn_detach_allchans(struct hn_softc *sc)
636215516c77SSepherosa Ziehau {
636315516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
636415516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
636515516c77SSepherosa Ziehau 	int i;
636615516c77SSepherosa Ziehau 
636715516c77SSepherosa Ziehau 	if (subchan_cnt == 0)
636815516c77SSepherosa Ziehau 		goto back;
636915516c77SSepherosa Ziehau 
637015516c77SSepherosa Ziehau 	/* Detach the sub-channels. */
637115516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
637215516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i)
637315516c77SSepherosa Ziehau 		hn_chan_detach(sc, subchans[i]);
637415516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
637515516c77SSepherosa Ziehau 
637615516c77SSepherosa Ziehau back:
637715516c77SSepherosa Ziehau 	/*
637815516c77SSepherosa Ziehau 	 * Detach the primary channel, _after_ all sub-channels
637915516c77SSepherosa Ziehau 	 * are detached.
638015516c77SSepherosa Ziehau 	 */
638115516c77SSepherosa Ziehau 	hn_chan_detach(sc, sc->hn_prichan);
638215516c77SSepherosa Ziehau 
638315516c77SSepherosa Ziehau 	/* Wait for sub-channels to be destroyed, if any. */
638415516c77SSepherosa Ziehau 	vmbus_subchan_drain(sc->hn_prichan);
638515516c77SSepherosa Ziehau 
638615516c77SSepherosa Ziehau #ifdef INVARIANTS
638715516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
638815516c77SSepherosa Ziehau 		KASSERT((sc->hn_rx_ring[i].hn_rx_flags &
638915516c77SSepherosa Ziehau 		    HN_RX_FLAG_ATTACHED) == 0,
639015516c77SSepherosa Ziehau 		    ("%dth RX ring is still attached", i));
639115516c77SSepherosa Ziehau 	}
639215516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
639315516c77SSepherosa Ziehau 		KASSERT((sc->hn_tx_ring[i].hn_tx_flags &
639415516c77SSepherosa Ziehau 		    HN_TX_FLAG_ATTACHED) == 0,
639515516c77SSepherosa Ziehau 		    ("%dth TX ring is still attached", i));
639615516c77SSepherosa Ziehau 	}
639715516c77SSepherosa Ziehau #endif
639815516c77SSepherosa Ziehau }
639915516c77SSepherosa Ziehau 
640015516c77SSepherosa Ziehau static int
640115516c77SSepherosa Ziehau hn_synth_alloc_subchans(struct hn_softc *sc, int *nsubch)
640215516c77SSepherosa Ziehau {
640315516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
640415516c77SSepherosa Ziehau 	int nchan, rxr_cnt, error;
640515516c77SSepherosa Ziehau 
640615516c77SSepherosa Ziehau 	nchan = *nsubch + 1;
640715516c77SSepherosa Ziehau 	if (nchan == 1) {
640815516c77SSepherosa Ziehau 		/*
640915516c77SSepherosa Ziehau 		 * Multiple RX/TX rings are not requested.
641015516c77SSepherosa Ziehau 		 */
641115516c77SSepherosa Ziehau 		*nsubch = 0;
641215516c77SSepherosa Ziehau 		return (0);
641315516c77SSepherosa Ziehau 	}
641415516c77SSepherosa Ziehau 
641515516c77SSepherosa Ziehau 	/*
641615516c77SSepherosa Ziehau 	 * Query RSS capabilities, e.g. # of RX rings, and # of indirect
641715516c77SSepherosa Ziehau 	 * table entries.
641815516c77SSepherosa Ziehau 	 */
641915516c77SSepherosa Ziehau 	error = hn_rndis_query_rsscaps(sc, &rxr_cnt);
642015516c77SSepherosa Ziehau 	if (error) {
642115516c77SSepherosa Ziehau 		/* No RSS; this is benign. */
642215516c77SSepherosa Ziehau 		*nsubch = 0;
642315516c77SSepherosa Ziehau 		return (0);
642415516c77SSepherosa Ziehau 	}
642515516c77SSepherosa Ziehau 	if (bootverbose) {
642615516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
642715516c77SSepherosa Ziehau 		    rxr_cnt, nchan);
642815516c77SSepherosa Ziehau 	}
642915516c77SSepherosa Ziehau 
643015516c77SSepherosa Ziehau 	if (nchan > rxr_cnt)
643115516c77SSepherosa Ziehau 		nchan = rxr_cnt;
643215516c77SSepherosa Ziehau 	if (nchan == 1) {
643315516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "only 1 channel is supported, no vRSS\n");
643415516c77SSepherosa Ziehau 		*nsubch = 0;
643515516c77SSepherosa Ziehau 		return (0);
643615516c77SSepherosa Ziehau 	}
643715516c77SSepherosa Ziehau 
643815516c77SSepherosa Ziehau 	/*
643915516c77SSepherosa Ziehau 	 * Allocate sub-channels from NVS.
644015516c77SSepherosa Ziehau 	 */
644115516c77SSepherosa Ziehau 	*nsubch = nchan - 1;
644215516c77SSepherosa Ziehau 	error = hn_nvs_alloc_subchans(sc, nsubch);
644315516c77SSepherosa Ziehau 	if (error || *nsubch == 0) {
644415516c77SSepherosa Ziehau 		/* Failed to allocate sub-channels. */
644515516c77SSepherosa Ziehau 		*nsubch = 0;
644615516c77SSepherosa Ziehau 		return (0);
644715516c77SSepherosa Ziehau 	}
644815516c77SSepherosa Ziehau 
644915516c77SSepherosa Ziehau 	/*
645015516c77SSepherosa Ziehau 	 * Wait for all sub-channels to become ready before moving on.
645115516c77SSepherosa Ziehau 	 */
645215516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, *nsubch);
645315516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, *nsubch);
645415516c77SSepherosa Ziehau 	return (0);
645515516c77SSepherosa Ziehau }
645615516c77SSepherosa Ziehau 
64572494d735SSepherosa Ziehau static bool
64582494d735SSepherosa Ziehau hn_synth_attachable(const struct hn_softc *sc)
64592494d735SSepherosa Ziehau {
64602494d735SSepherosa Ziehau 	int i;
64612494d735SSepherosa Ziehau 
64622494d735SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_ERRORS)
64632494d735SSepherosa Ziehau 		return (false);
64642494d735SSepherosa Ziehau 
64652494d735SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
64662494d735SSepherosa Ziehau 		const struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
64672494d735SSepherosa Ziehau 
64682494d735SSepherosa Ziehau 		if (rxr->hn_rx_flags & HN_RX_FLAG_BR_REF)
64692494d735SSepherosa Ziehau 			return (false);
64702494d735SSepherosa Ziehau 	}
64712494d735SSepherosa Ziehau 	return (true);
64722494d735SSepherosa Ziehau }
64732494d735SSepherosa Ziehau 
6474b3b75d9cSSepherosa Ziehau /*
6475b3b75d9cSSepherosa Ziehau  * Make sure that the RX filter is zero after the successful
6476b3b75d9cSSepherosa Ziehau  * RNDIS initialization.
6477b3b75d9cSSepherosa Ziehau  *
6478b3b75d9cSSepherosa Ziehau  * NOTE:
6479b3b75d9cSSepherosa Ziehau  * Under certain conditions on certain versions of Hyper-V,
6480b3b75d9cSSepherosa Ziehau  * the RNDIS rxfilter is _not_ zero on the hypervisor side
6481b3b75d9cSSepherosa Ziehau  * after the successful RNDIS initialization, which breaks
6482b3b75d9cSSepherosa Ziehau  * the assumption of any following code (well, it breaks the
6483b3b75d9cSSepherosa Ziehau  * RNDIS API contract actually).  Clear the RNDIS rxfilter
6484b3b75d9cSSepherosa Ziehau  * explicitly, drain packets sneaking through, and drain the
6485b3b75d9cSSepherosa Ziehau  * interrupt taskqueues scheduled due to the stealth packets.
6486b3b75d9cSSepherosa Ziehau  */
6487b3b75d9cSSepherosa Ziehau static void
6488b3b75d9cSSepherosa Ziehau hn_rndis_init_fixat(struct hn_softc *sc, int nchan)
6489b3b75d9cSSepherosa Ziehau {
6490b3b75d9cSSepherosa Ziehau 
6491b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
6492b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, nchan);
6493b3b75d9cSSepherosa Ziehau }
6494b3b75d9cSSepherosa Ziehau 
649515516c77SSepherosa Ziehau static int
649615516c77SSepherosa Ziehau hn_synth_attach(struct hn_softc *sc, int mtu)
649715516c77SSepherosa Ziehau {
649871e8ac56SSepherosa Ziehau #define ATTACHED_NVS		0x0002
649971e8ac56SSepherosa Ziehau #define ATTACHED_RNDIS		0x0004
650071e8ac56SSepherosa Ziehau 
650115516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
6502b3b75d9cSSepherosa Ziehau 	int error, nsubch, nchan = 1, i, rndis_inited;
650371e8ac56SSepherosa Ziehau 	uint32_t old_caps, attached = 0;
650415516c77SSepherosa Ziehau 
650515516c77SSepherosa Ziehau 	KASSERT((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0,
650615516c77SSepherosa Ziehau 	    ("synthetic parts were attached"));
650715516c77SSepherosa Ziehau 
65082494d735SSepherosa Ziehau 	if (!hn_synth_attachable(sc))
65092494d735SSepherosa Ziehau 		return (ENXIO);
65102494d735SSepherosa Ziehau 
651115516c77SSepherosa Ziehau 	/* Save capabilities for later verification. */
651215516c77SSepherosa Ziehau 	old_caps = sc->hn_caps;
651315516c77SSepherosa Ziehau 	sc->hn_caps = 0;
651415516c77SSepherosa Ziehau 
651515516c77SSepherosa Ziehau 	/* Clear RSS stuffs. */
651615516c77SSepherosa Ziehau 	sc->hn_rss_ind_size = 0;
651715516c77SSepherosa Ziehau 	sc->hn_rss_hash = 0;
6518642ec226SSepherosa Ziehau 	sc->hn_rss_hcap = 0;
651915516c77SSepherosa Ziehau 
652015516c77SSepherosa Ziehau 	/*
652115516c77SSepherosa Ziehau 	 * Attach the primary channel _before_ attaching NVS and RNDIS.
652215516c77SSepherosa Ziehau 	 */
652315516c77SSepherosa Ziehau 	error = hn_chan_attach(sc, sc->hn_prichan);
652415516c77SSepherosa Ziehau 	if (error)
652571e8ac56SSepherosa Ziehau 		goto failed;
652615516c77SSepherosa Ziehau 
652715516c77SSepherosa Ziehau 	/*
652815516c77SSepherosa Ziehau 	 * Attach NVS.
652915516c77SSepherosa Ziehau 	 */
653015516c77SSepherosa Ziehau 	error = hn_nvs_attach(sc, mtu);
653115516c77SSepherosa Ziehau 	if (error)
653271e8ac56SSepherosa Ziehau 		goto failed;
653371e8ac56SSepherosa Ziehau 	attached |= ATTACHED_NVS;
653415516c77SSepherosa Ziehau 
653515516c77SSepherosa Ziehau 	/*
653615516c77SSepherosa Ziehau 	 * Attach RNDIS _after_ NVS is attached.
653715516c77SSepherosa Ziehau 	 */
6538b3b75d9cSSepherosa Ziehau 	error = hn_rndis_attach(sc, mtu, &rndis_inited);
6539b3b75d9cSSepherosa Ziehau 	if (rndis_inited)
6540b3b75d9cSSepherosa Ziehau 		attached |= ATTACHED_RNDIS;
654115516c77SSepherosa Ziehau 	if (error)
654271e8ac56SSepherosa Ziehau 		goto failed;
654315516c77SSepherosa Ziehau 
654415516c77SSepherosa Ziehau 	/*
654515516c77SSepherosa Ziehau 	 * Make sure capabilities are not changed.
654615516c77SSepherosa Ziehau 	 */
654715516c77SSepherosa Ziehau 	if (device_is_attached(sc->hn_dev) && old_caps != sc->hn_caps) {
654815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "caps mismatch old 0x%08x, new 0x%08x\n",
654915516c77SSepherosa Ziehau 		    old_caps, sc->hn_caps);
655071e8ac56SSepherosa Ziehau 		error = ENXIO;
655171e8ac56SSepherosa Ziehau 		goto failed;
655215516c77SSepherosa Ziehau 	}
655315516c77SSepherosa Ziehau 
655415516c77SSepherosa Ziehau 	/*
655515516c77SSepherosa Ziehau 	 * Allocate sub-channels for multi-TX/RX rings.
655615516c77SSepherosa Ziehau 	 *
655715516c77SSepherosa Ziehau 	 * NOTE:
655815516c77SSepherosa Ziehau 	 * The # of RX rings that can be used is equivalent to the # of
655915516c77SSepherosa Ziehau 	 * channels to be requested.
656015516c77SSepherosa Ziehau 	 */
656115516c77SSepherosa Ziehau 	nsubch = sc->hn_rx_ring_cnt - 1;
656215516c77SSepherosa Ziehau 	error = hn_synth_alloc_subchans(sc, &nsubch);
656315516c77SSepherosa Ziehau 	if (error)
656471e8ac56SSepherosa Ziehau 		goto failed;
656571e8ac56SSepherosa Ziehau 	/* NOTE: _Full_ synthetic parts detach is required now. */
656671e8ac56SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_SYNTH_ATTACHED;
656715516c77SSepherosa Ziehau 
656871e8ac56SSepherosa Ziehau 	/*
656971e8ac56SSepherosa Ziehau 	 * Set the # of TX/RX rings that could be used according to
657071e8ac56SSepherosa Ziehau 	 * the # of channels that NVS offered.
657171e8ac56SSepherosa Ziehau 	 */
657215516c77SSepherosa Ziehau 	nchan = nsubch + 1;
657371e8ac56SSepherosa Ziehau 	hn_set_ring_inuse(sc, nchan);
657415516c77SSepherosa Ziehau 	if (nchan == 1) {
657515516c77SSepherosa Ziehau 		/* Only the primary channel can be used; done */
657615516c77SSepherosa Ziehau 		goto back;
657715516c77SSepherosa Ziehau 	}
657815516c77SSepherosa Ziehau 
657915516c77SSepherosa Ziehau 	/*
658071e8ac56SSepherosa Ziehau 	 * Attach the sub-channels.
6581afd4971bSSepherosa Ziehau 	 *
6582afd4971bSSepherosa Ziehau 	 * NOTE: hn_set_ring_inuse() _must_ have been called.
658315516c77SSepherosa Ziehau 	 */
658471e8ac56SSepherosa Ziehau 	error = hn_attach_subchans(sc);
658571e8ac56SSepherosa Ziehau 	if (error)
658671e8ac56SSepherosa Ziehau 		goto failed;
658715516c77SSepherosa Ziehau 
658871e8ac56SSepherosa Ziehau 	/*
658971e8ac56SSepherosa Ziehau 	 * Configure RSS key and indirect table _after_ all sub-channels
659071e8ac56SSepherosa Ziehau 	 * are attached.
659171e8ac56SSepherosa Ziehau 	 */
659215516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSKEY) == 0) {
659315516c77SSepherosa Ziehau 		/*
659415516c77SSepherosa Ziehau 		 * RSS key is not set yet; set it to the default RSS key.
659515516c77SSepherosa Ziehau 		 */
659615516c77SSepherosa Ziehau 		if (bootverbose)
659715516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS key\n");
659834d68912SSepherosa Ziehau #ifdef RSS
659934d68912SSepherosa Ziehau 		rss_getkey(rss->rss_key);
660034d68912SSepherosa Ziehau #else
660115516c77SSepherosa Ziehau 		memcpy(rss->rss_key, hn_rss_key_default, sizeof(rss->rss_key));
660234d68912SSepherosa Ziehau #endif
660315516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
660415516c77SSepherosa Ziehau 	}
660515516c77SSepherosa Ziehau 
660615516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSIND) == 0) {
660715516c77SSepherosa Ziehau 		/*
660815516c77SSepherosa Ziehau 		 * RSS indirect table is not set yet; set it up in round-
660915516c77SSepherosa Ziehau 		 * robin fashion.
661015516c77SSepherosa Ziehau 		 */
661115516c77SSepherosa Ziehau 		if (bootverbose) {
661215516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS indirect "
661315516c77SSepherosa Ziehau 			    "table\n");
661415516c77SSepherosa Ziehau 		}
661534d68912SSepherosa Ziehau 		for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
661634d68912SSepherosa Ziehau 			uint32_t subidx;
661734d68912SSepherosa Ziehau 
661834d68912SSepherosa Ziehau #ifdef RSS
661934d68912SSepherosa Ziehau 			subidx = rss_get_indirection_to_bucket(i);
662034d68912SSepherosa Ziehau #else
662134d68912SSepherosa Ziehau 			subidx = i;
662234d68912SSepherosa Ziehau #endif
662334d68912SSepherosa Ziehau 			rss->rss_ind[i] = subidx % nchan;
662434d68912SSepherosa Ziehau 		}
662515516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSIND;
662615516c77SSepherosa Ziehau 	} else {
662715516c77SSepherosa Ziehau 		/*
662815516c77SSepherosa Ziehau 		 * # of usable channels may be changed, so we have to
662915516c77SSepherosa Ziehau 		 * make sure that all entries in RSS indirect table
663015516c77SSepherosa Ziehau 		 * are valid.
6631afd4971bSSepherosa Ziehau 		 *
6632afd4971bSSepherosa Ziehau 		 * NOTE: hn_set_ring_inuse() _must_ have been called.
663315516c77SSepherosa Ziehau 		 */
6634afd4971bSSepherosa Ziehau 		hn_rss_ind_fixup(sc);
663515516c77SSepherosa Ziehau 	}
663615516c77SSepherosa Ziehau 
6637642ec226SSepherosa Ziehau 	sc->hn_rss_hash = sc->hn_rss_hcap;
6638642ec226SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) ||
6639642ec226SSepherosa Ziehau 	    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
6640642ec226SSepherosa Ziehau 		/* NOTE: Don't reconfigure RSS; will do immediately. */
6641642ec226SSepherosa Ziehau 		hn_vf_rss_fixup(sc, false);
6642642ec226SSepherosa Ziehau 	}
664315516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
664415516c77SSepherosa Ziehau 	if (error)
664571e8ac56SSepherosa Ziehau 		goto failed;
664671e8ac56SSepherosa Ziehau back:
6647dc13fee6SSepherosa Ziehau 	/*
6648dc13fee6SSepherosa Ziehau 	 * Fixup transmission aggregation setup.
6649dc13fee6SSepherosa Ziehau 	 */
6650dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
6651b3b75d9cSSepherosa Ziehau 	hn_rndis_init_fixat(sc, nchan);
665215516c77SSepherosa Ziehau 	return (0);
665371e8ac56SSepherosa Ziehau 
665471e8ac56SSepherosa Ziehau failed:
665571e8ac56SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
6656b3b75d9cSSepherosa Ziehau 		hn_rndis_init_fixat(sc, nchan);
665771e8ac56SSepherosa Ziehau 		hn_synth_detach(sc);
665871e8ac56SSepherosa Ziehau 	} else {
6659b3b75d9cSSepherosa Ziehau 		if (attached & ATTACHED_RNDIS) {
6660b3b75d9cSSepherosa Ziehau 			hn_rndis_init_fixat(sc, nchan);
666171e8ac56SSepherosa Ziehau 			hn_rndis_detach(sc);
6662b3b75d9cSSepherosa Ziehau 		}
666371e8ac56SSepherosa Ziehau 		if (attached & ATTACHED_NVS)
666471e8ac56SSepherosa Ziehau 			hn_nvs_detach(sc);
666571e8ac56SSepherosa Ziehau 		hn_chan_detach(sc, sc->hn_prichan);
666671e8ac56SSepherosa Ziehau 		/* Restore old capabilities. */
666771e8ac56SSepherosa Ziehau 		sc->hn_caps = old_caps;
666871e8ac56SSepherosa Ziehau 	}
666971e8ac56SSepherosa Ziehau 	return (error);
667071e8ac56SSepherosa Ziehau 
667171e8ac56SSepherosa Ziehau #undef ATTACHED_RNDIS
667271e8ac56SSepherosa Ziehau #undef ATTACHED_NVS
667315516c77SSepherosa Ziehau }
667415516c77SSepherosa Ziehau 
667515516c77SSepherosa Ziehau /*
667615516c77SSepherosa Ziehau  * NOTE:
667715516c77SSepherosa Ziehau  * The interface must have been suspended though hn_suspend(), before
667815516c77SSepherosa Ziehau  * this function get called.
667915516c77SSepherosa Ziehau  */
668015516c77SSepherosa Ziehau static void
668115516c77SSepherosa Ziehau hn_synth_detach(struct hn_softc *sc)
668215516c77SSepherosa Ziehau {
668315516c77SSepherosa Ziehau 
668415516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
668515516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
668615516c77SSepherosa Ziehau 
668715516c77SSepherosa Ziehau 	/* Detach the RNDIS first. */
668815516c77SSepherosa Ziehau 	hn_rndis_detach(sc);
668915516c77SSepherosa Ziehau 
669015516c77SSepherosa Ziehau 	/* Detach NVS. */
669115516c77SSepherosa Ziehau 	hn_nvs_detach(sc);
669215516c77SSepherosa Ziehau 
669315516c77SSepherosa Ziehau 	/* Detach all of the channels. */
669415516c77SSepherosa Ziehau 	hn_detach_allchans(sc);
669515516c77SSepherosa Ziehau 
6696ace5ce7eSWei Hu 	if (vmbus_current_version >= VMBUS_VERSION_WIN10 && sc->hn_rxbuf_gpadl != 0) {
6697ace5ce7eSWei Hu 		/*
6698ace5ce7eSWei Hu 		 * Host is post-Win2016, disconnect RXBUF from primary channel here.
6699ace5ce7eSWei Hu 		 */
6700ace5ce7eSWei Hu 		int error;
6701ace5ce7eSWei Hu 
6702ace5ce7eSWei Hu 		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
6703ace5ce7eSWei Hu 		    sc->hn_rxbuf_gpadl);
6704ace5ce7eSWei Hu 		if (error) {
6705ace5ce7eSWei Hu 			if_printf(sc->hn_ifp,
6706ace5ce7eSWei Hu 			    "rxbuf gpadl disconn failed: %d\n", error);
6707ace5ce7eSWei Hu 			sc->hn_flags |= HN_FLAG_RXBUF_REF;
6708ace5ce7eSWei Hu 		}
6709ace5ce7eSWei Hu 		sc->hn_rxbuf_gpadl = 0;
6710ace5ce7eSWei Hu 	}
6711ace5ce7eSWei Hu 
6712ace5ce7eSWei Hu 	if (vmbus_current_version >= VMBUS_VERSION_WIN10 && sc->hn_chim_gpadl != 0) {
6713ace5ce7eSWei Hu 		/*
6714ace5ce7eSWei Hu 		 * Host is post-Win2016, disconnect chimney sending buffer from
6715ace5ce7eSWei Hu 		 * primary channel here.
6716ace5ce7eSWei Hu 		 */
6717ace5ce7eSWei Hu 		int error;
6718ace5ce7eSWei Hu 
6719ace5ce7eSWei Hu 		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
6720ace5ce7eSWei Hu 		    sc->hn_chim_gpadl);
6721ace5ce7eSWei Hu 		if (error) {
6722ace5ce7eSWei Hu 			if_printf(sc->hn_ifp,
6723ace5ce7eSWei Hu 			    "chim gpadl disconn failed: %d\n", error);
6724ace5ce7eSWei Hu 			sc->hn_flags |= HN_FLAG_CHIM_REF;
6725ace5ce7eSWei Hu 		}
6726ace5ce7eSWei Hu 		sc->hn_chim_gpadl = 0;
6727ace5ce7eSWei Hu 	}
672815516c77SSepherosa Ziehau 	sc->hn_flags &= ~HN_FLAG_SYNTH_ATTACHED;
672915516c77SSepherosa Ziehau }
673015516c77SSepherosa Ziehau 
673115516c77SSepherosa Ziehau static void
673215516c77SSepherosa Ziehau hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt)
673315516c77SSepherosa Ziehau {
673415516c77SSepherosa Ziehau 	KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_cnt,
673515516c77SSepherosa Ziehau 	    ("invalid ring count %d", ring_cnt));
673615516c77SSepherosa Ziehau 
673715516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt > ring_cnt)
673815516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = ring_cnt;
673915516c77SSepherosa Ziehau 	else
674015516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
674115516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = ring_cnt;
674215516c77SSepherosa Ziehau 
674334d68912SSepherosa Ziehau #ifdef RSS
674434d68912SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse != rss_getnumbuckets()) {
674534d68912SSepherosa Ziehau 		if_printf(sc->hn_ifp, "# of RX rings (%d) does not match "
674634d68912SSepherosa Ziehau 		    "# of RSS buckets (%d)\n", sc->hn_rx_ring_inuse,
674734d68912SSepherosa Ziehau 		    rss_getnumbuckets());
674834d68912SSepherosa Ziehau 	}
674934d68912SSepherosa Ziehau #endif
675034d68912SSepherosa Ziehau 
675115516c77SSepherosa Ziehau 	if (bootverbose) {
675215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n",
675315516c77SSepherosa Ziehau 		    sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
675415516c77SSepherosa Ziehau 	}
675515516c77SSepherosa Ziehau }
675615516c77SSepherosa Ziehau 
675715516c77SSepherosa Ziehau static void
675825641fc7SSepherosa Ziehau hn_chan_drain(struct hn_softc *sc, struct vmbus_channel *chan)
675915516c77SSepherosa Ziehau {
676015516c77SSepherosa Ziehau 
676125641fc7SSepherosa Ziehau 	/*
676225641fc7SSepherosa Ziehau 	 * NOTE:
676325641fc7SSepherosa Ziehau 	 * The TX bufring will not be drained by the hypervisor,
676425641fc7SSepherosa Ziehau 	 * if the primary channel is revoked.
676525641fc7SSepherosa Ziehau 	 */
676625641fc7SSepherosa Ziehau 	while (!vmbus_chan_rx_empty(chan) ||
676725641fc7SSepherosa Ziehau 	    (!vmbus_chan_is_revoked(sc->hn_prichan) &&
676825641fc7SSepherosa Ziehau 	     !vmbus_chan_tx_empty(chan)))
676915516c77SSepherosa Ziehau 		pause("waitch", 1);
677015516c77SSepherosa Ziehau 	vmbus_chan_intr_drain(chan);
677115516c77SSepherosa Ziehau }
677215516c77SSepherosa Ziehau 
677315516c77SSepherosa Ziehau static void
6774b3b75d9cSSepherosa Ziehau hn_disable_rx(struct hn_softc *sc)
6775b3b75d9cSSepherosa Ziehau {
6776b3b75d9cSSepherosa Ziehau 
6777b3b75d9cSSepherosa Ziehau 	/*
6778b3b75d9cSSepherosa Ziehau 	 * Disable RX by clearing RX filter forcefully.
6779b3b75d9cSSepherosa Ziehau 	 */
6780b3b75d9cSSepherosa Ziehau 	sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE;
6781b3b75d9cSSepherosa Ziehau 	hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); /* ignore error */
6782b3b75d9cSSepherosa Ziehau 
6783b3b75d9cSSepherosa Ziehau 	/*
6784b3b75d9cSSepherosa Ziehau 	 * Give RNDIS enough time to flush all pending data packets.
6785b3b75d9cSSepherosa Ziehau 	 */
6786b3b75d9cSSepherosa Ziehau 	pause("waitrx", (200 * hz) / 1000);
6787b3b75d9cSSepherosa Ziehau }
6788b3b75d9cSSepherosa Ziehau 
6789b3b75d9cSSepherosa Ziehau /*
6790b3b75d9cSSepherosa Ziehau  * NOTE:
6791b3b75d9cSSepherosa Ziehau  * RX/TX _must_ have been suspended/disabled, before this function
6792b3b75d9cSSepherosa Ziehau  * is called.
6793b3b75d9cSSepherosa Ziehau  */
6794b3b75d9cSSepherosa Ziehau static void
6795b3b75d9cSSepherosa Ziehau hn_drain_rxtx(struct hn_softc *sc, int nchan)
679615516c77SSepherosa Ziehau {
679715516c77SSepherosa Ziehau 	struct vmbus_channel **subch = NULL;
6798b3b75d9cSSepherosa Ziehau 	int nsubch;
6799b3b75d9cSSepherosa Ziehau 
6800b3b75d9cSSepherosa Ziehau 	/*
6801b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX bufrings and interrupts.
6802b3b75d9cSSepherosa Ziehau 	 */
6803b3b75d9cSSepherosa Ziehau 	nsubch = nchan - 1;
6804b3b75d9cSSepherosa Ziehau 	if (nsubch > 0)
6805b3b75d9cSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
6806b3b75d9cSSepherosa Ziehau 
6807b3b75d9cSSepherosa Ziehau 	if (subch != NULL) {
6808b3b75d9cSSepherosa Ziehau 		int i;
6809b3b75d9cSSepherosa Ziehau 
6810b3b75d9cSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
6811b3b75d9cSSepherosa Ziehau 			hn_chan_drain(sc, subch[i]);
6812b3b75d9cSSepherosa Ziehau 	}
6813b3b75d9cSSepherosa Ziehau 	hn_chan_drain(sc, sc->hn_prichan);
6814b3b75d9cSSepherosa Ziehau 
6815b3b75d9cSSepherosa Ziehau 	if (subch != NULL)
6816b3b75d9cSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
6817b3b75d9cSSepherosa Ziehau }
6818b3b75d9cSSepherosa Ziehau 
6819b3b75d9cSSepherosa Ziehau static void
6820b3b75d9cSSepherosa Ziehau hn_suspend_data(struct hn_softc *sc)
6821b3b75d9cSSepherosa Ziehau {
682225641fc7SSepherosa Ziehau 	struct hn_tx_ring *txr;
6823b3b75d9cSSepherosa Ziehau 	int i;
682415516c77SSepherosa Ziehau 
682515516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
682615516c77SSepherosa Ziehau 
682715516c77SSepherosa Ziehau 	/*
682815516c77SSepherosa Ziehau 	 * Suspend TX.
682915516c77SSepherosa Ziehau 	 */
683015516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
683125641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
683215516c77SSepherosa Ziehau 
683315516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
683415516c77SSepherosa Ziehau 		txr->hn_suspended = 1;
683515516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
683615516c77SSepherosa Ziehau 		/* No one is able send more packets now. */
683715516c77SSepherosa Ziehau 
683825641fc7SSepherosa Ziehau 		/*
683925641fc7SSepherosa Ziehau 		 * Wait for all pending sends to finish.
684025641fc7SSepherosa Ziehau 		 *
684125641fc7SSepherosa Ziehau 		 * NOTE:
684225641fc7SSepherosa Ziehau 		 * We will _not_ receive all pending send-done, if the
684325641fc7SSepherosa Ziehau 		 * primary channel is revoked.
684425641fc7SSepherosa Ziehau 		 */
684525641fc7SSepherosa Ziehau 		while (hn_tx_ring_pending(txr) &&
684625641fc7SSepherosa Ziehau 		    !vmbus_chan_is_revoked(sc->hn_prichan))
684715516c77SSepherosa Ziehau 			pause("hnwtx", 1 /* 1 tick */);
684815516c77SSepherosa Ziehau 	}
684915516c77SSepherosa Ziehau 
685015516c77SSepherosa Ziehau 	/*
6851b3b75d9cSSepherosa Ziehau 	 * Disable RX.
685215516c77SSepherosa Ziehau 	 */
6853b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
685415516c77SSepherosa Ziehau 
685515516c77SSepherosa Ziehau 	/*
6856b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX.
685715516c77SSepherosa Ziehau 	 */
6858b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, sc->hn_rx_ring_inuse);
685925641fc7SSepherosa Ziehau 
686025641fc7SSepherosa Ziehau 	/*
686125641fc7SSepherosa Ziehau 	 * Drain any pending TX tasks.
686225641fc7SSepherosa Ziehau 	 *
686325641fc7SSepherosa Ziehau 	 * NOTE:
6864b3b75d9cSSepherosa Ziehau 	 * The above hn_drain_rxtx() can dispatch TX tasks, so the TX
6865b3b75d9cSSepherosa Ziehau 	 * tasks will have to be drained _after_ the above hn_drain_rxtx().
686625641fc7SSepherosa Ziehau 	 */
686725641fc7SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
686825641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
686925641fc7SSepherosa Ziehau 
687025641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task);
687125641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task);
687225641fc7SSepherosa Ziehau 	}
687315516c77SSepherosa Ziehau }
687415516c77SSepherosa Ziehau 
687515516c77SSepherosa Ziehau static void
687615516c77SSepherosa Ziehau hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused)
687715516c77SSepherosa Ziehau {
687815516c77SSepherosa Ziehau 
687915516c77SSepherosa Ziehau 	((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL;
688015516c77SSepherosa Ziehau }
688115516c77SSepherosa Ziehau 
688215516c77SSepherosa Ziehau static void
688315516c77SSepherosa Ziehau hn_suspend_mgmt(struct hn_softc *sc)
688415516c77SSepherosa Ziehau {
688515516c77SSepherosa Ziehau 	struct task task;
688615516c77SSepherosa Ziehau 
688715516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
688815516c77SSepherosa Ziehau 
688915516c77SSepherosa Ziehau 	/*
689015516c77SSepherosa Ziehau 	 * Make sure that hn_mgmt_taskq0 can nolonger be accessed
689115516c77SSepherosa Ziehau 	 * through hn_mgmt_taskq.
689215516c77SSepherosa Ziehau 	 */
689315516c77SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_suspend_mgmt_taskfunc, sc);
689415516c77SSepherosa Ziehau 	vmbus_chan_run_task(sc->hn_prichan, &task);
689515516c77SSepherosa Ziehau 
689615516c77SSepherosa Ziehau 	/*
689715516c77SSepherosa Ziehau 	 * Make sure that all pending management tasks are completed.
689815516c77SSepherosa Ziehau 	 */
689915516c77SSepherosa Ziehau 	taskqueue_drain(sc->hn_mgmt_taskq0, &sc->hn_netchg_init);
690015516c77SSepherosa Ziehau 	taskqueue_drain_timeout(sc->hn_mgmt_taskq0, &sc->hn_netchg_status);
690115516c77SSepherosa Ziehau 	taskqueue_drain_all(sc->hn_mgmt_taskq0);
690215516c77SSepherosa Ziehau }
690315516c77SSepherosa Ziehau 
690415516c77SSepherosa Ziehau static void
690515516c77SSepherosa Ziehau hn_suspend(struct hn_softc *sc)
690615516c77SSepherosa Ziehau {
690715516c77SSepherosa Ziehau 
690887f8129dSSepherosa Ziehau 	/* Disable polling. */
690987f8129dSSepherosa Ziehau 	hn_polling(sc, 0);
691087f8129dSSepherosa Ziehau 
69119c6cae24SSepherosa Ziehau 	/*
69129c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
69139c6cae24SSepherosa Ziehau 	 * device is receiving packets, so the data path of the
69149c6cae24SSepherosa Ziehau 	 * synthetic device must be suspended.
69159c6cae24SSepherosa Ziehau 	 */
6916*4db5958aSJustin Hibbits 	if ((if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) ||
6917962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
691815516c77SSepherosa Ziehau 		hn_suspend_data(sc);
691915516c77SSepherosa Ziehau 	hn_suspend_mgmt(sc);
692015516c77SSepherosa Ziehau }
692115516c77SSepherosa Ziehau 
692215516c77SSepherosa Ziehau static void
692315516c77SSepherosa Ziehau hn_resume_tx(struct hn_softc *sc, int tx_ring_cnt)
692415516c77SSepherosa Ziehau {
692515516c77SSepherosa Ziehau 	int i;
692615516c77SSepherosa Ziehau 
692715516c77SSepherosa Ziehau 	KASSERT(tx_ring_cnt <= sc->hn_tx_ring_cnt,
692815516c77SSepherosa Ziehau 	    ("invalid TX ring count %d", tx_ring_cnt));
692915516c77SSepherosa Ziehau 
693015516c77SSepherosa Ziehau 	for (i = 0; i < tx_ring_cnt; ++i) {
693115516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
693215516c77SSepherosa Ziehau 
693315516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
693415516c77SSepherosa Ziehau 		txr->hn_suspended = 0;
693515516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
693615516c77SSepherosa Ziehau 	}
693715516c77SSepherosa Ziehau }
693815516c77SSepherosa Ziehau 
693915516c77SSepherosa Ziehau static void
694015516c77SSepherosa Ziehau hn_resume_data(struct hn_softc *sc)
694115516c77SSepherosa Ziehau {
694215516c77SSepherosa Ziehau 	int i;
694315516c77SSepherosa Ziehau 
694415516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
694515516c77SSepherosa Ziehau 
694615516c77SSepherosa Ziehau 	/*
694715516c77SSepherosa Ziehau 	 * Re-enable RX.
694815516c77SSepherosa Ziehau 	 */
6949c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
695015516c77SSepherosa Ziehau 
695115516c77SSepherosa Ziehau 	/*
695215516c77SSepherosa Ziehau 	 * Make sure to clear suspend status on "all" TX rings,
695315516c77SSepherosa Ziehau 	 * since hn_tx_ring_inuse can be changed after
695415516c77SSepherosa Ziehau 	 * hn_suspend_data().
695515516c77SSepherosa Ziehau 	 */
695615516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_cnt);
695715516c77SSepherosa Ziehau 
695823bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
695923bf9e15SSepherosa Ziehau 	if (!hn_use_if_start)
696023bf9e15SSepherosa Ziehau #endif
696123bf9e15SSepherosa Ziehau 	{
696215516c77SSepherosa Ziehau 		/*
696315516c77SSepherosa Ziehau 		 * Flush unused drbrs, since hn_tx_ring_inuse may be
696415516c77SSepherosa Ziehau 		 * reduced.
696515516c77SSepherosa Ziehau 		 */
696615516c77SSepherosa Ziehau 		for (i = sc->hn_tx_ring_inuse; i < sc->hn_tx_ring_cnt; ++i)
696715516c77SSepherosa Ziehau 			hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
696815516c77SSepherosa Ziehau 	}
696915516c77SSepherosa Ziehau 
697015516c77SSepherosa Ziehau 	/*
697115516c77SSepherosa Ziehau 	 * Kick start TX.
697215516c77SSepherosa Ziehau 	 */
697315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
697415516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
697515516c77SSepherosa Ziehau 
697615516c77SSepherosa Ziehau 		/*
697715516c77SSepherosa Ziehau 		 * Use txeof task, so that any pending oactive can be
697815516c77SSepherosa Ziehau 		 * cleared properly.
697915516c77SSepherosa Ziehau 		 */
698015516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
698115516c77SSepherosa Ziehau 	}
698215516c77SSepherosa Ziehau }
698315516c77SSepherosa Ziehau 
698415516c77SSepherosa Ziehau static void
698515516c77SSepherosa Ziehau hn_resume_mgmt(struct hn_softc *sc)
698615516c77SSepherosa Ziehau {
698715516c77SSepherosa Ziehau 
698815516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
698915516c77SSepherosa Ziehau 
699015516c77SSepherosa Ziehau 	/*
699115516c77SSepherosa Ziehau 	 * Kick off network change detection, if it was pending.
699215516c77SSepherosa Ziehau 	 * If no network change was pending, start link status
699315516c77SSepherosa Ziehau 	 * checks, which is more lightweight than network change
699415516c77SSepherosa Ziehau 	 * detection.
699515516c77SSepherosa Ziehau 	 */
699615516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
699715516c77SSepherosa Ziehau 		hn_change_network(sc);
699815516c77SSepherosa Ziehau 	else
699915516c77SSepherosa Ziehau 		hn_update_link_status(sc);
700015516c77SSepherosa Ziehau }
700115516c77SSepherosa Ziehau 
700215516c77SSepherosa Ziehau static void
700315516c77SSepherosa Ziehau hn_resume(struct hn_softc *sc)
700415516c77SSepherosa Ziehau {
700515516c77SSepherosa Ziehau 
70069c6cae24SSepherosa Ziehau 	/*
70079c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
70089c6cae24SSepherosa Ziehau 	 * device have to receive packets, so the data path of the
70099c6cae24SSepherosa Ziehau 	 * synthetic device must be resumed.
70109c6cae24SSepherosa Ziehau 	 */
7011*4db5958aSJustin Hibbits 	if ((if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) ||
7012962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
701315516c77SSepherosa Ziehau 		hn_resume_data(sc);
70145bdfd3fdSDexuan Cui 
70155bdfd3fdSDexuan Cui 	/*
70169c6cae24SSepherosa Ziehau 	 * Don't resume link status change if VF is attached/activated.
70179c6cae24SSepherosa Ziehau 	 * - In the non-transparent VF mode, the synthetic device marks
70189c6cae24SSepherosa Ziehau 	 *   link down until the VF is deactivated; i.e. VF is down.
70199c6cae24SSepherosa Ziehau 	 * - In transparent VF mode, VF's media status is used until
70209c6cae24SSepherosa Ziehau 	 *   the VF is detached.
70215bdfd3fdSDexuan Cui 	 */
70229c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) == 0 &&
70239c6cae24SSepherosa Ziehau 	    !(hn_xpnt_vf && sc->hn_vf_ifp != NULL))
702415516c77SSepherosa Ziehau 		hn_resume_mgmt(sc);
702587f8129dSSepherosa Ziehau 
702687f8129dSSepherosa Ziehau 	/*
702787f8129dSSepherosa Ziehau 	 * Re-enable polling if this interface is running and
702887f8129dSSepherosa Ziehau 	 * the polling is requested.
702987f8129dSSepherosa Ziehau 	 */
7030*4db5958aSJustin Hibbits 	if ((if_getdrvflags(sc->hn_ifp) & IFF_DRV_RUNNING) && sc->hn_pollhz > 0)
703187f8129dSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
703215516c77SSepherosa Ziehau }
703315516c77SSepherosa Ziehau 
703415516c77SSepherosa Ziehau static void
703515516c77SSepherosa Ziehau hn_rndis_rx_status(struct hn_softc *sc, const void *data, int dlen)
703615516c77SSepherosa Ziehau {
703715516c77SSepherosa Ziehau 	const struct rndis_status_msg *msg;
703815516c77SSepherosa Ziehau 	int ofs;
703915516c77SSepherosa Ziehau 
704015516c77SSepherosa Ziehau 	if (dlen < sizeof(*msg)) {
704115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid RNDIS status\n");
704215516c77SSepherosa Ziehau 		return;
704315516c77SSepherosa Ziehau 	}
704415516c77SSepherosa Ziehau 	msg = data;
704515516c77SSepherosa Ziehau 
704615516c77SSepherosa Ziehau 	switch (msg->rm_status) {
704715516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_CONNECT:
704815516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_DISCONNECT:
704915516c77SSepherosa Ziehau 		hn_update_link_status(sc);
705015516c77SSepherosa Ziehau 		break;
705115516c77SSepherosa Ziehau 
705215516c77SSepherosa Ziehau 	case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
705340905afaSSepherosa Ziehau 	case RNDIS_STATUS_LINK_SPEED_CHANGE:
705415516c77SSepherosa Ziehau 		/* Not really useful; ignore. */
705515516c77SSepherosa Ziehau 		break;
705615516c77SSepherosa Ziehau 
705715516c77SSepherosa Ziehau 	case RNDIS_STATUS_NETWORK_CHANGE:
705815516c77SSepherosa Ziehau 		ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
705915516c77SSepherosa Ziehau 		if (dlen < ofs + msg->rm_stbuflen ||
706015516c77SSepherosa Ziehau 		    msg->rm_stbuflen < sizeof(uint32_t)) {
706115516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed\n");
706215516c77SSepherosa Ziehau 		} else {
706315516c77SSepherosa Ziehau 			uint32_t change;
706415516c77SSepherosa Ziehau 
706515516c77SSepherosa Ziehau 			memcpy(&change, ((const uint8_t *)msg) + ofs,
706615516c77SSepherosa Ziehau 			    sizeof(change));
706715516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed, change %u\n",
706815516c77SSepherosa Ziehau 			    change);
706915516c77SSepherosa Ziehau 		}
707015516c77SSepherosa Ziehau 		hn_change_network(sc);
707115516c77SSepherosa Ziehau 		break;
707215516c77SSepherosa Ziehau 
707315516c77SSepherosa Ziehau 	default:
707415516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
707515516c77SSepherosa Ziehau 		    msg->rm_status);
707615516c77SSepherosa Ziehau 		break;
707715516c77SSepherosa Ziehau 	}
707815516c77SSepherosa Ziehau }
707915516c77SSepherosa Ziehau 
708015516c77SSepherosa Ziehau static int
708115516c77SSepherosa Ziehau hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info)
708215516c77SSepherosa Ziehau {
708315516c77SSepherosa Ziehau 	const struct rndis_pktinfo *pi = info_data;
708415516c77SSepherosa Ziehau 	uint32_t mask = 0;
708515516c77SSepherosa Ziehau 
708615516c77SSepherosa Ziehau 	while (info_dlen != 0) {
708715516c77SSepherosa Ziehau 		const void *data;
708815516c77SSepherosa Ziehau 		uint32_t dlen;
708915516c77SSepherosa Ziehau 
709015516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < sizeof(*pi)))
709115516c77SSepherosa Ziehau 			return (EINVAL);
709215516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < pi->rm_size))
709315516c77SSepherosa Ziehau 			return (EINVAL);
709415516c77SSepherosa Ziehau 		info_dlen -= pi->rm_size;
709515516c77SSepherosa Ziehau 
709615516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
709715516c77SSepherosa Ziehau 			return (EINVAL);
709815516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
709915516c77SSepherosa Ziehau 			return (EINVAL);
710015516c77SSepherosa Ziehau 		dlen = pi->rm_size - pi->rm_pktinfooffset;
710115516c77SSepherosa Ziehau 		data = pi->rm_data;
710215516c77SSepherosa Ziehau 
7103a491581fSWei Hu 		if (pi->rm_internal == 1) {
7104a491581fSWei Hu 			switch (pi->rm_type) {
7105a491581fSWei Hu 			case NDIS_PKTINFO_IT_PKTINFO_ID:
7106a491581fSWei Hu 				if (__predict_false(dlen < NDIS_PKTINFOID_SZ))
7107a491581fSWei Hu 					return (EINVAL);
7108a491581fSWei Hu 				info->pktinfo_id =
7109a491581fSWei Hu 				    (const struct packet_info_id *)data;
7110a491581fSWei Hu 				mask |= HN_RXINFO_PKTINFO_ID;
7111a491581fSWei Hu 				break;
7112a491581fSWei Hu 
7113a491581fSWei Hu 			default:
7114a491581fSWei Hu 				goto next;
7115a491581fSWei Hu 			}
7116a491581fSWei Hu 		} else {
711715516c77SSepherosa Ziehau 			switch (pi->rm_type) {
711815516c77SSepherosa Ziehau 			case NDIS_PKTINFO_TYPE_VLAN:
7119a491581fSWei Hu 				if (__predict_false(dlen
7120a491581fSWei Hu 				    < NDIS_VLAN_INFO_SIZE))
712115516c77SSepherosa Ziehau 					return (EINVAL);
7122a491581fSWei Hu 				info->vlan_info = (const uint32_t *)data;
712315516c77SSepherosa Ziehau 				mask |= HN_RXINFO_VLAN;
712415516c77SSepherosa Ziehau 				break;
712515516c77SSepherosa Ziehau 
712615516c77SSepherosa Ziehau 			case NDIS_PKTINFO_TYPE_CSUM:
7127a491581fSWei Hu 				if (__predict_false(dlen
7128a491581fSWei Hu 				    < NDIS_RXCSUM_INFO_SIZE))
712915516c77SSepherosa Ziehau 					return (EINVAL);
7130a491581fSWei Hu 				info->csum_info = (const uint32_t *)data;
713115516c77SSepherosa Ziehau 				mask |= HN_RXINFO_CSUM;
713215516c77SSepherosa Ziehau 				break;
713315516c77SSepherosa Ziehau 
713415516c77SSepherosa Ziehau 			case HN_NDIS_PKTINFO_TYPE_HASHVAL:
7135a491581fSWei Hu 				if (__predict_false(dlen
7136a491581fSWei Hu 				    < HN_NDIS_HASH_VALUE_SIZE))
713715516c77SSepherosa Ziehau 					return (EINVAL);
7138a491581fSWei Hu 				info->hash_value = (const uint32_t *)data;
713915516c77SSepherosa Ziehau 				mask |= HN_RXINFO_HASHVAL;
714015516c77SSepherosa Ziehau 				break;
714115516c77SSepherosa Ziehau 
714215516c77SSepherosa Ziehau 			case HN_NDIS_PKTINFO_TYPE_HASHINF:
7143a491581fSWei Hu 				if (__predict_false(dlen
7144a491581fSWei Hu 				    < HN_NDIS_HASH_INFO_SIZE))
714515516c77SSepherosa Ziehau 					return (EINVAL);
7146a491581fSWei Hu 				info->hash_info = (const uint32_t *)data;
714715516c77SSepherosa Ziehau 				mask |= HN_RXINFO_HASHINF;
714815516c77SSepherosa Ziehau 				break;
714915516c77SSepherosa Ziehau 
715015516c77SSepherosa Ziehau 			default:
715115516c77SSepherosa Ziehau 				goto next;
715215516c77SSepherosa Ziehau 			}
7153a491581fSWei Hu 		}
715415516c77SSepherosa Ziehau 
715515516c77SSepherosa Ziehau 		if (mask == HN_RXINFO_ALL) {
715615516c77SSepherosa Ziehau 			/* All found; done */
715715516c77SSepherosa Ziehau 			break;
715815516c77SSepherosa Ziehau 		}
715915516c77SSepherosa Ziehau next:
716015516c77SSepherosa Ziehau 		pi = (const struct rndis_pktinfo *)
716115516c77SSepherosa Ziehau 		    ((const uint8_t *)pi + pi->rm_size);
716215516c77SSepherosa Ziehau 	}
716315516c77SSepherosa Ziehau 
716415516c77SSepherosa Ziehau 	/*
716515516c77SSepherosa Ziehau 	 * Final fixup.
716615516c77SSepherosa Ziehau 	 * - If there is no hash value, invalidate the hash info.
716715516c77SSepherosa Ziehau 	 */
716815516c77SSepherosa Ziehau 	if ((mask & HN_RXINFO_HASHVAL) == 0)
7169a491581fSWei Hu 		info->hash_info = NULL;
717015516c77SSepherosa Ziehau 	return (0);
717115516c77SSepherosa Ziehau }
717215516c77SSepherosa Ziehau 
717315516c77SSepherosa Ziehau static __inline bool
717415516c77SSepherosa Ziehau hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
717515516c77SSepherosa Ziehau {
717615516c77SSepherosa Ziehau 
717715516c77SSepherosa Ziehau 	if (off < check_off) {
717815516c77SSepherosa Ziehau 		if (__predict_true(off + len <= check_off))
717915516c77SSepherosa Ziehau 			return (false);
718015516c77SSepherosa Ziehau 	} else if (off > check_off) {
718115516c77SSepherosa Ziehau 		if (__predict_true(check_off + check_len <= off))
718215516c77SSepherosa Ziehau 			return (false);
718315516c77SSepherosa Ziehau 	}
718415516c77SSepherosa Ziehau 	return (true);
718515516c77SSepherosa Ziehau }
718615516c77SSepherosa Ziehau 
7187a491581fSWei Hu static __inline void
7188a491581fSWei Hu hn_rsc_add_data(struct hn_rx_ring *rxr, const void *data,
7189a491581fSWei Hu 		uint32_t len, struct hn_rxinfo *info)
7190a491581fSWei Hu {
7191a491581fSWei Hu 	uint32_t cnt = rxr->rsc.cnt;
7192a491581fSWei Hu 
7193a491581fSWei Hu 	if (cnt) {
7194a491581fSWei Hu 		rxr->rsc.pktlen += len;
7195a491581fSWei Hu 	} else {
7196a491581fSWei Hu 		rxr->rsc.vlan_info = info->vlan_info;
7197a491581fSWei Hu 		rxr->rsc.csum_info = info->csum_info;
7198a491581fSWei Hu 		rxr->rsc.hash_info = info->hash_info;
7199a491581fSWei Hu 		rxr->rsc.hash_value = info->hash_value;
7200a491581fSWei Hu 		rxr->rsc.pktlen = len;
7201a491581fSWei Hu 	}
7202a491581fSWei Hu 
7203a491581fSWei Hu 	rxr->rsc.frag_data[cnt] = data;
7204a491581fSWei Hu 	rxr->rsc.frag_len[cnt] = len;
7205a491581fSWei Hu 	rxr->rsc.cnt++;
7206a491581fSWei Hu }
7207a491581fSWei Hu 
720815516c77SSepherosa Ziehau static void
720915516c77SSepherosa Ziehau hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen)
721015516c77SSepherosa Ziehau {
721115516c77SSepherosa Ziehau 	const struct rndis_packet_msg *pkt;
721215516c77SSepherosa Ziehau 	struct hn_rxinfo info;
721315516c77SSepherosa Ziehau 	int data_off, pktinfo_off, data_len, pktinfo_len;
7214a491581fSWei Hu 	bool rsc_more= false;
721515516c77SSepherosa Ziehau 
721615516c77SSepherosa Ziehau 	/*
721715516c77SSepherosa Ziehau 	 * Check length.
721815516c77SSepherosa Ziehau 	 */
721915516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*pkt))) {
722015516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
722115516c77SSepherosa Ziehau 		return;
722215516c77SSepherosa Ziehau 	}
722315516c77SSepherosa Ziehau 	pkt = data;
722415516c77SSepherosa Ziehau 
722515516c77SSepherosa Ziehau 	if (__predict_false(dlen < pkt->rm_len)) {
722615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
722715516c77SSepherosa Ziehau 		    "dlen %d, msglen %u\n", dlen, pkt->rm_len);
722815516c77SSepherosa Ziehau 		return;
722915516c77SSepherosa Ziehau 	}
723015516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_len <
723115516c77SSepherosa Ziehau 	    pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
723215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
723315516c77SSepherosa Ziehau 		    "msglen %u, data %u, oob %u, pktinfo %u\n",
723415516c77SSepherosa Ziehau 		    pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
723515516c77SSepherosa Ziehau 		    pkt->rm_pktinfolen);
723615516c77SSepherosa Ziehau 		return;
723715516c77SSepherosa Ziehau 	}
723815516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_datalen == 0)) {
723915516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
724015516c77SSepherosa Ziehau 		return;
724115516c77SSepherosa Ziehau 	}
724215516c77SSepherosa Ziehau 
724315516c77SSepherosa Ziehau 	/*
724415516c77SSepherosa Ziehau 	 * Check offests.
724515516c77SSepherosa Ziehau 	 */
724615516c77SSepherosa Ziehau #define IS_OFFSET_INVALID(ofs)			\
724715516c77SSepherosa Ziehau 	((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN ||	\
724815516c77SSepherosa Ziehau 	 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
724915516c77SSepherosa Ziehau 
725015516c77SSepherosa Ziehau 	/* XXX Hyper-V does not meet data offset alignment requirement */
725115516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
725215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
725315516c77SSepherosa Ziehau 		    "data offset %u\n", pkt->rm_dataoffset);
725415516c77SSepherosa Ziehau 		return;
725515516c77SSepherosa Ziehau 	}
725615516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdataoffset > 0 &&
725715516c77SSepherosa Ziehau 	    IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
725815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
725915516c77SSepherosa Ziehau 		    "oob offset %u\n", pkt->rm_oobdataoffset);
726015516c77SSepherosa Ziehau 		return;
726115516c77SSepherosa Ziehau 	}
726215516c77SSepherosa Ziehau 	if (__predict_true(pkt->rm_pktinfooffset > 0) &&
726315516c77SSepherosa Ziehau 	    __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
726415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
726515516c77SSepherosa Ziehau 		    "pktinfo offset %u\n", pkt->rm_pktinfooffset);
726615516c77SSepherosa Ziehau 		return;
726715516c77SSepherosa Ziehau 	}
726815516c77SSepherosa Ziehau 
726915516c77SSepherosa Ziehau #undef IS_OFFSET_INVALID
727015516c77SSepherosa Ziehau 
727115516c77SSepherosa Ziehau 	data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
727215516c77SSepherosa Ziehau 	data_len = pkt->rm_datalen;
727315516c77SSepherosa Ziehau 	pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
727415516c77SSepherosa Ziehau 	pktinfo_len = pkt->rm_pktinfolen;
727515516c77SSepherosa Ziehau 
727615516c77SSepherosa Ziehau 	/*
727715516c77SSepherosa Ziehau 	 * Check OOB coverage.
727815516c77SSepherosa Ziehau 	 */
727915516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdatalen != 0)) {
728015516c77SSepherosa Ziehau 		int oob_off, oob_len;
728115516c77SSepherosa Ziehau 
728215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "got oobdata\n");
728315516c77SSepherosa Ziehau 		oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
728415516c77SSepherosa Ziehau 		oob_len = pkt->rm_oobdatalen;
728515516c77SSepherosa Ziehau 
728615516c77SSepherosa Ziehau 		if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
728715516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
728815516c77SSepherosa Ziehau 			    "oob overflow, msglen %u, oob abs %d len %d\n",
728915516c77SSepherosa Ziehau 			    pkt->rm_len, oob_off, oob_len);
729015516c77SSepherosa Ziehau 			return;
729115516c77SSepherosa Ziehau 		}
729215516c77SSepherosa Ziehau 
729315516c77SSepherosa Ziehau 		/*
729415516c77SSepherosa Ziehau 		 * Check against data.
729515516c77SSepherosa Ziehau 		 */
729615516c77SSepherosa Ziehau 		if (hn_rndis_check_overlap(oob_off, oob_len,
729715516c77SSepherosa Ziehau 		    data_off, data_len)) {
729815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
729915516c77SSepherosa Ziehau 			    "oob overlaps data, oob abs %d len %d, "
730015516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
730115516c77SSepherosa Ziehau 			    oob_off, oob_len, data_off, data_len);
730215516c77SSepherosa Ziehau 			return;
730315516c77SSepherosa Ziehau 		}
730415516c77SSepherosa Ziehau 
730515516c77SSepherosa Ziehau 		/*
730615516c77SSepherosa Ziehau 		 * Check against pktinfo.
730715516c77SSepherosa Ziehau 		 */
730815516c77SSepherosa Ziehau 		if (pktinfo_len != 0 &&
730915516c77SSepherosa Ziehau 		    hn_rndis_check_overlap(oob_off, oob_len,
731015516c77SSepherosa Ziehau 		    pktinfo_off, pktinfo_len)) {
731115516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
731215516c77SSepherosa Ziehau 			    "oob overlaps pktinfo, oob abs %d len %d, "
731315516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
731415516c77SSepherosa Ziehau 			    oob_off, oob_len, pktinfo_off, pktinfo_len);
731515516c77SSepherosa Ziehau 			return;
731615516c77SSepherosa Ziehau 		}
731715516c77SSepherosa Ziehau 	}
731815516c77SSepherosa Ziehau 
731915516c77SSepherosa Ziehau 	/*
732015516c77SSepherosa Ziehau 	 * Check per-packet-info coverage and find useful per-packet-info.
732115516c77SSepherosa Ziehau 	 */
7322a491581fSWei Hu 	info.vlan_info = NULL;
7323a491581fSWei Hu 	info.csum_info = NULL;
7324a491581fSWei Hu 	info.hash_info = NULL;
7325a491581fSWei Hu 	info.pktinfo_id = NULL;
7326a491581fSWei Hu 
732715516c77SSepherosa Ziehau 	if (__predict_true(pktinfo_len != 0)) {
732815516c77SSepherosa Ziehau 		bool overlap;
732915516c77SSepherosa Ziehau 		int error;
733015516c77SSepherosa Ziehau 
733115516c77SSepherosa Ziehau 		if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
733215516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
733315516c77SSepherosa Ziehau 			    "pktinfo overflow, msglen %u, "
733415516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
733515516c77SSepherosa Ziehau 			    pkt->rm_len, pktinfo_off, pktinfo_len);
733615516c77SSepherosa Ziehau 			return;
733715516c77SSepherosa Ziehau 		}
733815516c77SSepherosa Ziehau 
733915516c77SSepherosa Ziehau 		/*
734015516c77SSepherosa Ziehau 		 * Check packet info coverage.
734115516c77SSepherosa Ziehau 		 */
734215516c77SSepherosa Ziehau 		overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
734315516c77SSepherosa Ziehau 		    data_off, data_len);
734415516c77SSepherosa Ziehau 		if (__predict_false(overlap)) {
734515516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
734615516c77SSepherosa Ziehau 			    "pktinfo overlap data, pktinfo abs %d len %d, "
734715516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
734815516c77SSepherosa Ziehau 			    pktinfo_off, pktinfo_len, data_off, data_len);
734915516c77SSepherosa Ziehau 			return;
735015516c77SSepherosa Ziehau 		}
735115516c77SSepherosa Ziehau 
735215516c77SSepherosa Ziehau 		/*
735315516c77SSepherosa Ziehau 		 * Find useful per-packet-info.
735415516c77SSepherosa Ziehau 		 */
735515516c77SSepherosa Ziehau 		error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
735615516c77SSepherosa Ziehau 		    pktinfo_len, &info);
735715516c77SSepherosa Ziehau 		if (__predict_false(error)) {
735815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
735915516c77SSepherosa Ziehau 			    "pktinfo\n");
736015516c77SSepherosa Ziehau 			return;
736115516c77SSepherosa Ziehau 		}
736215516c77SSepherosa Ziehau 	}
736315516c77SSepherosa Ziehau 
736415516c77SSepherosa Ziehau 	if (__predict_false(data_off + data_len > pkt->rm_len)) {
736515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
736615516c77SSepherosa Ziehau 		    "data overflow, msglen %u, data abs %d len %d\n",
736715516c77SSepherosa Ziehau 		    pkt->rm_len, data_off, data_len);
736815516c77SSepherosa Ziehau 		return;
736915516c77SSepherosa Ziehau 	}
7370a491581fSWei Hu 
7371a491581fSWei Hu 	/* Identify RSC fragments, drop invalid packets */
7372a491581fSWei Hu 	if ((info.pktinfo_id != NULL) &&
7373a491581fSWei Hu 	    (info.pktinfo_id->flag & HN_NDIS_PKTINFO_SUBALLOC)) {
7374a491581fSWei Hu 		if (info.pktinfo_id->flag & HN_NDIS_PKTINFO_1ST_FRAG) {
7375a491581fSWei Hu 			rxr->rsc.cnt = 0;
7376a491581fSWei Hu 			rxr->hn_rsc_pkts++;
7377a491581fSWei Hu 		} else if (rxr->rsc.cnt == 0)
7378a491581fSWei Hu 			goto drop;
7379a491581fSWei Hu 
7380a491581fSWei Hu 		rsc_more = true;
7381a491581fSWei Hu 
7382a491581fSWei Hu 		if (info.pktinfo_id->flag & HN_NDIS_PKTINFO_LAST_FRAG)
7383a491581fSWei Hu 			rsc_more = false;
7384a491581fSWei Hu 
7385a491581fSWei Hu 		if (rsc_more && rxr->rsc.is_last)
7386a491581fSWei Hu 			goto drop;
7387a491581fSWei Hu 	} else {
7388a491581fSWei Hu 		rxr->rsc.cnt = 0;
7389a491581fSWei Hu 	}
7390a491581fSWei Hu 
7391a491581fSWei Hu 	if (__predict_false(rxr->rsc.cnt >= HN_NVS_RSC_MAX))
7392a491581fSWei Hu 		goto drop;
7393a491581fSWei Hu 
7394a491581fSWei Hu 	/* Store data in per rx ring structure */
7395a491581fSWei Hu 	hn_rsc_add_data(rxr,((const uint8_t *)pkt) + data_off,
7396a491581fSWei Hu 	    data_len, &info);
7397a491581fSWei Hu 
7398a491581fSWei Hu 	if (rsc_more)
7399a491581fSWei Hu 		return;
7400a491581fSWei Hu 
7401a491581fSWei Hu 	hn_rxpkt(rxr);
7402a491581fSWei Hu 	rxr->rsc.cnt = 0;
7403a491581fSWei Hu 	return;
7404a491581fSWei Hu drop:
7405a491581fSWei Hu 	rxr->hn_rsc_drop++;
7406a491581fSWei Hu 	return;
740715516c77SSepherosa Ziehau }
740815516c77SSepherosa Ziehau 
740915516c77SSepherosa Ziehau static __inline void
741015516c77SSepherosa Ziehau hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen)
741115516c77SSepherosa Ziehau {
741215516c77SSepherosa Ziehau 	const struct rndis_msghdr *hdr;
741315516c77SSepherosa Ziehau 
741415516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*hdr))) {
741515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
741615516c77SSepherosa Ziehau 		return;
741715516c77SSepherosa Ziehau 	}
741815516c77SSepherosa Ziehau 	hdr = data;
741915516c77SSepherosa Ziehau 
742015516c77SSepherosa Ziehau 	if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) {
742115516c77SSepherosa Ziehau 		/* Hot data path. */
742215516c77SSepherosa Ziehau 		hn_rndis_rx_data(rxr, data, dlen);
742315516c77SSepherosa Ziehau 		/* Done! */
742415516c77SSepherosa Ziehau 		return;
742515516c77SSepherosa Ziehau 	}
742615516c77SSepherosa Ziehau 
742715516c77SSepherosa Ziehau 	if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG)
7428*4db5958aSJustin Hibbits 		hn_rndis_rx_status(if_getsoftc(rxr->hn_ifp), data, dlen);
742915516c77SSepherosa Ziehau 	else
7430*4db5958aSJustin Hibbits 		hn_rndis_rx_ctrl(if_getsoftc(rxr->hn_ifp), data, dlen);
743115516c77SSepherosa Ziehau }
743215516c77SSepherosa Ziehau 
743315516c77SSepherosa Ziehau static void
743415516c77SSepherosa Ziehau hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
743515516c77SSepherosa Ziehau {
743615516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *hdr;
743715516c77SSepherosa Ziehau 
743815516c77SSepherosa Ziehau 	if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) {
743915516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid nvs notify\n");
744015516c77SSepherosa Ziehau 		return;
744115516c77SSepherosa Ziehau 	}
744215516c77SSepherosa Ziehau 	hdr = VMBUS_CHANPKT_CONST_DATA(pkt);
744315516c77SSepherosa Ziehau 
744415516c77SSepherosa Ziehau 	if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) {
744515516c77SSepherosa Ziehau 		/* Useless; ignore */
744615516c77SSepherosa Ziehau 		return;
744715516c77SSepherosa Ziehau 	}
744815516c77SSepherosa Ziehau 	if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type);
744915516c77SSepherosa Ziehau }
745015516c77SSepherosa Ziehau 
745115516c77SSepherosa Ziehau static void
745215516c77SSepherosa Ziehau hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
745315516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkt)
745415516c77SSepherosa Ziehau {
745515516c77SSepherosa Ziehau 	struct hn_nvs_sendctx *sndc;
745615516c77SSepherosa Ziehau 
745715516c77SSepherosa Ziehau 	sndc = (struct hn_nvs_sendctx *)(uintptr_t)pkt->cph_xactid;
745815516c77SSepherosa Ziehau 	sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt),
745915516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_DATALEN(pkt));
746015516c77SSepherosa Ziehau 	/*
746115516c77SSepherosa Ziehau 	 * NOTE:
746215516c77SSepherosa Ziehau 	 * 'sndc' CAN NOT be accessed anymore, since it can be freed by
746315516c77SSepherosa Ziehau 	 * its callback.
746415516c77SSepherosa Ziehau 	 */
746515516c77SSepherosa Ziehau }
746615516c77SSepherosa Ziehau 
746715516c77SSepherosa Ziehau static void
746815516c77SSepherosa Ziehau hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
746915516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkthdr)
747015516c77SSepherosa Ziehau {
747126d79d40SMichael Tuexen 	struct epoch_tracker et;
747215516c77SSepherosa Ziehau 	const struct vmbus_chanpkt_rxbuf *pkt;
747315516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *nvs_hdr;
747415516c77SSepherosa Ziehau 	int count, i, hlen;
747515516c77SSepherosa Ziehau 
747615516c77SSepherosa Ziehau 	if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) {
747715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n");
747815516c77SSepherosa Ziehau 		return;
747915516c77SSepherosa Ziehau 	}
748015516c77SSepherosa Ziehau 	nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr);
748115516c77SSepherosa Ziehau 
748215516c77SSepherosa Ziehau 	/* Make sure that this is a RNDIS message. */
748315516c77SSepherosa Ziehau 	if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) {
748415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n",
748515516c77SSepherosa Ziehau 		    nvs_hdr->nvs_type);
748615516c77SSepherosa Ziehau 		return;
748715516c77SSepherosa Ziehau 	}
748815516c77SSepherosa Ziehau 
748915516c77SSepherosa Ziehau 	hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen);
749015516c77SSepherosa Ziehau 	if (__predict_false(hlen < sizeof(*pkt))) {
749115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n");
749215516c77SSepherosa Ziehau 		return;
749315516c77SSepherosa Ziehau 	}
749415516c77SSepherosa Ziehau 	pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr;
749515516c77SSepherosa Ziehau 
749615516c77SSepherosa Ziehau 	if (__predict_false(pkt->cp_rxbuf_id != HN_NVS_RXBUF_SIG)) {
749715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n",
749815516c77SSepherosa Ziehau 		    pkt->cp_rxbuf_id);
749915516c77SSepherosa Ziehau 		return;
750015516c77SSepherosa Ziehau 	}
750115516c77SSepherosa Ziehau 
750215516c77SSepherosa Ziehau 	count = pkt->cp_rxbuf_cnt;
750315516c77SSepherosa Ziehau 	if (__predict_false(hlen <
750415516c77SSepherosa Ziehau 	    __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) {
750515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count);
750615516c77SSepherosa Ziehau 		return;
750715516c77SSepherosa Ziehau 	}
750815516c77SSepherosa Ziehau 
750926d79d40SMichael Tuexen 	NET_EPOCH_ENTER(et);
751015516c77SSepherosa Ziehau 	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
751115516c77SSepherosa Ziehau 	for (i = 0; i < count; ++i) {
751215516c77SSepherosa Ziehau 		int ofs, len;
751315516c77SSepherosa Ziehau 
751415516c77SSepherosa Ziehau 		ofs = pkt->cp_rxbuf[i].rb_ofs;
751515516c77SSepherosa Ziehau 		len = pkt->cp_rxbuf[i].rb_len;
751615516c77SSepherosa Ziehau 		if (__predict_false(ofs + len > HN_RXBUF_SIZE)) {
751715516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, "
751815516c77SSepherosa Ziehau 			    "ofs %d, len %d\n", i, ofs, len);
751915516c77SSepherosa Ziehau 			continue;
752015516c77SSepherosa Ziehau 		}
7521a491581fSWei Hu 
7522a491581fSWei Hu 		rxr->rsc.is_last = (i == (count - 1));
752315516c77SSepherosa Ziehau 		hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len);
752415516c77SSepherosa Ziehau 	}
752526d79d40SMichael Tuexen 	NET_EPOCH_EXIT(et);
752615516c77SSepherosa Ziehau 
752715516c77SSepherosa Ziehau 	/*
752815516c77SSepherosa Ziehau 	 * Ack the consumed RXBUF associated w/ this channel packet,
752915516c77SSepherosa Ziehau 	 * so that this RXBUF can be recycled by the hypervisor.
753015516c77SSepherosa Ziehau 	 */
753115516c77SSepherosa Ziehau 	hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid);
753215516c77SSepherosa Ziehau }
753315516c77SSepherosa Ziehau 
753415516c77SSepherosa Ziehau static void
753515516c77SSepherosa Ziehau hn_nvs_ack_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
753615516c77SSepherosa Ziehau     uint64_t tid)
753715516c77SSepherosa Ziehau {
753815516c77SSepherosa Ziehau 	struct hn_nvs_rndis_ack ack;
753915516c77SSepherosa Ziehau 	int retries, error;
754015516c77SSepherosa Ziehau 
754115516c77SSepherosa Ziehau 	ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK;
754215516c77SSepherosa Ziehau 	ack.nvs_status = HN_NVS_STATUS_OK;
754315516c77SSepherosa Ziehau 
754415516c77SSepherosa Ziehau 	retries = 0;
754515516c77SSepherosa Ziehau again:
754615516c77SSepherosa Ziehau 	error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
754715516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid);
754815516c77SSepherosa Ziehau 	if (__predict_false(error == EAGAIN)) {
754915516c77SSepherosa Ziehau 		/*
755015516c77SSepherosa Ziehau 		 * NOTE:
755115516c77SSepherosa Ziehau 		 * This should _not_ happen in real world, since the
755215516c77SSepherosa Ziehau 		 * consumption of the TX bufring from the TX path is
755315516c77SSepherosa Ziehau 		 * controlled.
755415516c77SSepherosa Ziehau 		 */
755515516c77SSepherosa Ziehau 		if (rxr->hn_ack_failed == 0)
755615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "RXBUF ack retry\n");
755715516c77SSepherosa Ziehau 		rxr->hn_ack_failed++;
755815516c77SSepherosa Ziehau 		retries++;
755915516c77SSepherosa Ziehau 		if (retries < 10) {
756015516c77SSepherosa Ziehau 			DELAY(100);
756115516c77SSepherosa Ziehau 			goto again;
756215516c77SSepherosa Ziehau 		}
756315516c77SSepherosa Ziehau 		/* RXBUF leaks! */
756415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "RXBUF ack failed\n");
756515516c77SSepherosa Ziehau 	}
756615516c77SSepherosa Ziehau }
756715516c77SSepherosa Ziehau 
756815516c77SSepherosa Ziehau static void
756915516c77SSepherosa Ziehau hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
757015516c77SSepherosa Ziehau {
757115516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr = xrxr;
7572*4db5958aSJustin Hibbits 	struct hn_softc *sc = if_getsoftc(rxr->hn_ifp);
757315516c77SSepherosa Ziehau 
757415516c77SSepherosa Ziehau 	for (;;) {
757515516c77SSepherosa Ziehau 		struct vmbus_chanpkt_hdr *pkt = rxr->hn_pktbuf;
757615516c77SSepherosa Ziehau 		int error, pktlen;
757715516c77SSepherosa Ziehau 
757815516c77SSepherosa Ziehau 		pktlen = rxr->hn_pktbuf_len;
757915516c77SSepherosa Ziehau 		error = vmbus_chan_recv_pkt(chan, pkt, &pktlen);
758015516c77SSepherosa Ziehau 		if (__predict_false(error == ENOBUFS)) {
758115516c77SSepherosa Ziehau 			void *nbuf;
758215516c77SSepherosa Ziehau 			int nlen;
758315516c77SSepherosa Ziehau 
758415516c77SSepherosa Ziehau 			/*
758515516c77SSepherosa Ziehau 			 * Expand channel packet buffer.
758615516c77SSepherosa Ziehau 			 *
758715516c77SSepherosa Ziehau 			 * XXX
758815516c77SSepherosa Ziehau 			 * Use M_WAITOK here, since allocation failure
758915516c77SSepherosa Ziehau 			 * is fatal.
759015516c77SSepherosa Ziehau 			 */
759115516c77SSepherosa Ziehau 			nlen = rxr->hn_pktbuf_len * 2;
759215516c77SSepherosa Ziehau 			while (nlen < pktlen)
759315516c77SSepherosa Ziehau 				nlen *= 2;
759415516c77SSepherosa Ziehau 			nbuf = malloc(nlen, M_DEVBUF, M_WAITOK);
759515516c77SSepherosa Ziehau 
759615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "expand pktbuf %d -> %d\n",
759715516c77SSepherosa Ziehau 			    rxr->hn_pktbuf_len, nlen);
759815516c77SSepherosa Ziehau 
759915516c77SSepherosa Ziehau 			free(rxr->hn_pktbuf, M_DEVBUF);
760015516c77SSepherosa Ziehau 			rxr->hn_pktbuf = nbuf;
760115516c77SSepherosa Ziehau 			rxr->hn_pktbuf_len = nlen;
760215516c77SSepherosa Ziehau 			/* Retry! */
760315516c77SSepherosa Ziehau 			continue;
760415516c77SSepherosa Ziehau 		} else if (__predict_false(error == EAGAIN)) {
760515516c77SSepherosa Ziehau 			/* No more channel packets; done! */
760615516c77SSepherosa Ziehau 			break;
760715516c77SSepherosa Ziehau 		}
760815516c77SSepherosa Ziehau 		KASSERT(!error, ("vmbus_chan_recv_pkt failed: %d", error));
760915516c77SSepherosa Ziehau 
761015516c77SSepherosa Ziehau 		switch (pkt->cph_type) {
761115516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_COMP:
761215516c77SSepherosa Ziehau 			hn_nvs_handle_comp(sc, chan, pkt);
761315516c77SSepherosa Ziehau 			break;
761415516c77SSepherosa Ziehau 
761515516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_RXBUF:
761615516c77SSepherosa Ziehau 			hn_nvs_handle_rxbuf(rxr, chan, pkt);
761715516c77SSepherosa Ziehau 			break;
761815516c77SSepherosa Ziehau 
761915516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_INBAND:
762015516c77SSepherosa Ziehau 			hn_nvs_handle_notify(sc, pkt);
762115516c77SSepherosa Ziehau 			break;
762215516c77SSepherosa Ziehau 
762315516c77SSepherosa Ziehau 		default:
762415516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "unknown chan pkt %u\n",
762515516c77SSepherosa Ziehau 			    pkt->cph_type);
762615516c77SSepherosa Ziehau 			break;
762715516c77SSepherosa Ziehau 		}
762815516c77SSepherosa Ziehau 	}
762915516c77SSepherosa Ziehau 	hn_chan_rollup(rxr, rxr->hn_txr);
763015516c77SSepherosa Ziehau }
763115516c77SSepherosa Ziehau 
763215516c77SSepherosa Ziehau static void
7633499c3e17SSepherosa Ziehau hn_sysinit(void *arg __unused)
763415516c77SSepherosa Ziehau {
7635fdd0222aSSepherosa Ziehau 	int i;
7636fdd0222aSSepherosa Ziehau 
76372be266caSSepherosa Ziehau 	hn_udpcs_fixup = counter_u64_alloc(M_WAITOK);
76382be266caSSepherosa Ziehau 
76399c6cae24SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
76409c6cae24SSepherosa Ziehau 	/*
76419c6cae24SSepherosa Ziehau 	 * Don't use ifnet.if_start if transparent VF mode is requested;
76429c6cae24SSepherosa Ziehau 	 * mainly due to the IFF_DRV_OACTIVE flag.
76439c6cae24SSepherosa Ziehau 	 */
76449c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_use_if_start) {
76459c6cae24SSepherosa Ziehau 		hn_use_if_start = 0;
76469c6cae24SSepherosa Ziehau 		printf("hn: tranparent VF mode, if_transmit will be used, "
76479c6cae24SSepherosa Ziehau 		    "instead of if_start\n");
76489c6cae24SSepherosa Ziehau 	}
76499c6cae24SSepherosa Ziehau #endif
76509c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_attwait < HN_XPNT_VF_ATTWAIT_MIN) {
76519c6cae24SSepherosa Ziehau 		printf("hn: invalid transparent VF attach routing "
76529c6cae24SSepherosa Ziehau 		    "wait timeout %d, reset to %d\n",
76539c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_attwait, HN_XPNT_VF_ATTWAIT_MIN);
76549c6cae24SSepherosa Ziehau 		hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
76559c6cae24SSepherosa Ziehau 	}
76569c6cae24SSepherosa Ziehau 
7657fdd0222aSSepherosa Ziehau 	/*
7658499c3e17SSepherosa Ziehau 	 * Initialize VF map.
7659499c3e17SSepherosa Ziehau 	 */
7660499c3e17SSepherosa Ziehau 	rm_init_flags(&hn_vfmap_lock, "hn_vfmap", RM_SLEEPABLE);
7661499c3e17SSepherosa Ziehau 	hn_vfmap_size = HN_VFMAP_SIZE_DEF;
7662*4db5958aSJustin Hibbits 	hn_vfmap = malloc(sizeof(if_t) * hn_vfmap_size, M_DEVBUF,
7663499c3e17SSepherosa Ziehau 	    M_WAITOK | M_ZERO);
7664499c3e17SSepherosa Ziehau 
7665499c3e17SSepherosa Ziehau 	/*
7666fdd0222aSSepherosa Ziehau 	 * Fix the # of TX taskqueues.
7667fdd0222aSSepherosa Ziehau 	 */
7668fdd0222aSSepherosa Ziehau 	if (hn_tx_taskq_cnt <= 0)
7669fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = 1;
7670fdd0222aSSepherosa Ziehau 	else if (hn_tx_taskq_cnt > mp_ncpus)
7671fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = mp_ncpus;
767215516c77SSepherosa Ziehau 
76730e11868dSSepherosa Ziehau 	/*
76740e11868dSSepherosa Ziehau 	 * Fix the TX taskqueue mode.
76750e11868dSSepherosa Ziehau 	 */
76760e11868dSSepherosa Ziehau 	switch (hn_tx_taskq_mode) {
76770e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_INDEP:
76780e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_GLOBAL:
76790e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_EVTTQ:
76800e11868dSSepherosa Ziehau 		break;
76810e11868dSSepherosa Ziehau 	default:
76820e11868dSSepherosa Ziehau 		hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
76830e11868dSSepherosa Ziehau 		break;
76840e11868dSSepherosa Ziehau 	}
76850e11868dSSepherosa Ziehau 
768615516c77SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV)
768715516c77SSepherosa Ziehau 		return;
768815516c77SSepherosa Ziehau 
76890e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode != HN_TX_TASKQ_M_GLOBAL)
769015516c77SSepherosa Ziehau 		return;
769115516c77SSepherosa Ziehau 
7692fdd0222aSSepherosa Ziehau 	hn_tx_taskque = malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
7693fdd0222aSSepherosa Ziehau 	    M_DEVBUF, M_WAITOK);
7694fdd0222aSSepherosa Ziehau 	for (i = 0; i < hn_tx_taskq_cnt; ++i) {
7695fdd0222aSSepherosa Ziehau 		hn_tx_taskque[i] = taskqueue_create("hn_tx", M_WAITOK,
7696fdd0222aSSepherosa Ziehau 		    taskqueue_thread_enqueue, &hn_tx_taskque[i]);
7697fdd0222aSSepherosa Ziehau 		taskqueue_start_threads(&hn_tx_taskque[i], 1, PI_NET,
7698fdd0222aSSepherosa Ziehau 		    "hn tx%d", i);
7699fdd0222aSSepherosa Ziehau 	}
770015516c77SSepherosa Ziehau }
7701499c3e17SSepherosa Ziehau SYSINIT(hn_sysinit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysinit, NULL);
770215516c77SSepherosa Ziehau 
770315516c77SSepherosa Ziehau static void
7704499c3e17SSepherosa Ziehau hn_sysuninit(void *arg __unused)
770515516c77SSepherosa Ziehau {
770615516c77SSepherosa Ziehau 
7707fdd0222aSSepherosa Ziehau 	if (hn_tx_taskque != NULL) {
7708fdd0222aSSepherosa Ziehau 		int i;
7709fdd0222aSSepherosa Ziehau 
7710fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
7711fdd0222aSSepherosa Ziehau 			taskqueue_free(hn_tx_taskque[i]);
7712fdd0222aSSepherosa Ziehau 		free(hn_tx_taskque, M_DEVBUF);
7713fdd0222aSSepherosa Ziehau 	}
7714499c3e17SSepherosa Ziehau 
7715499c3e17SSepherosa Ziehau 	if (hn_vfmap != NULL)
7716499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
7717499c3e17SSepherosa Ziehau 	rm_destroy(&hn_vfmap_lock);
77182be266caSSepherosa Ziehau 
77192be266caSSepherosa Ziehau 	counter_u64_free(hn_udpcs_fixup);
772015516c77SSepherosa Ziehau }
7721499c3e17SSepherosa Ziehau SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL);
7722