xref: /freebsd/sys/dev/hyperv/netvsc/if_hn.c (revision b15a632c418f6532fe7c3002b4085c61b1e278b0)
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. */
16115516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MIN(ifp)		(2 * (ifp)->if_mtu)
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;
250962f0357SSepherosa Ziehau 	struct ifnet		*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 *);
27315516c77SSepherosa Ziehau static int			hn_ioctl(struct ifnet *, u_long, caddr_t);
27423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
27515516c77SSepherosa Ziehau static void			hn_start(struct ifnet *);
27623bf9e15SSepherosa Ziehau #endif
27715516c77SSepherosa Ziehau static int			hn_transmit(struct ifnet *, struct mbuf *);
27815516c77SSepherosa Ziehau static void			hn_xmit_qflush(struct ifnet *);
27915516c77SSepherosa Ziehau static int			hn_ifmedia_upd(struct ifnet *);
28015516c77SSepherosa Ziehau static void			hn_ifmedia_sts(struct ifnet *,
28115516c77SSepherosa Ziehau 				    struct ifmediareq *);
28215516c77SSepherosa Ziehau 
283499c3e17SSepherosa Ziehau static void			hn_ifnet_event(void *, struct ifnet *, int);
284499c3e17SSepherosa Ziehau static void			hn_ifaddr_event(void *, struct ifnet *);
285499c3e17SSepherosa Ziehau static void			hn_ifnet_attevent(void *, struct ifnet *);
286499c3e17SSepherosa Ziehau static void			hn_ifnet_detevent(void *, struct ifnet *);
2879c6cae24SSepherosa Ziehau static void			hn_ifnet_lnkevent(void *, struct ifnet *, int);
288499c3e17SSepherosa Ziehau 
289962f0357SSepherosa Ziehau static bool			hn_ismyvf(const struct hn_softc *,
290962f0357SSepherosa Ziehau 				    const struct ifnet *);
291962f0357SSepherosa Ziehau static void			hn_rxvf_change(struct hn_softc *,
292962f0357SSepherosa Ziehau 				    struct ifnet *, bool);
293962f0357SSepherosa Ziehau static void			hn_rxvf_set(struct hn_softc *, struct ifnet *);
294962f0357SSepherosa Ziehau static void			hn_rxvf_set_task(void *, int);
2959c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_input(struct ifnet *, 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 #if __FreeBSD_version >= 1100099
32915516c77SSepherosa Ziehau static int			hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS);
33015516c77SSepherosa Ziehau static int			hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS);
33115516c77SSepherosa Ziehau #endif
33215516c77SSepherosa Ziehau static int			hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS);
33315516c77SSepherosa Ziehau static int			hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS);
33415516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
33515516c77SSepherosa Ziehau static int			hn_rx_stat_int_sysctl(SYSCTL_HANDLER_ARGS);
33615516c77SSepherosa Ziehau #else
33715516c77SSepherosa Ziehau static int			hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS);
33815516c77SSepherosa Ziehau #endif
33915516c77SSepherosa Ziehau static int			hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
34015516c77SSepherosa Ziehau static int			hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
34115516c77SSepherosa Ziehau static int			hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS);
34215516c77SSepherosa Ziehau static int			hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS);
34315516c77SSepherosa Ziehau static int			hn_caps_sysctl(SYSCTL_HANDLER_ARGS);
34415516c77SSepherosa Ziehau static int			hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS);
34515516c77SSepherosa Ziehau static int			hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS);
34634d68912SSepherosa Ziehau #ifndef RSS
34715516c77SSepherosa Ziehau static int			hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS);
34815516c77SSepherosa Ziehau static int			hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS);
34934d68912SSepherosa Ziehau #endif
35015516c77SSepherosa Ziehau static int			hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS);
351642ec226SSepherosa Ziehau static int			hn_rss_hcap_sysctl(SYSCTL_HANDLER_ARGS);
352642ec226SSepherosa Ziehau static int			hn_rss_mbuf_sysctl(SYSCTL_HANDLER_ARGS);
353dc13fee6SSepherosa Ziehau static int			hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS);
354dc13fee6SSepherosa Ziehau static int			hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS);
355dc13fee6SSepherosa Ziehau static int			hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS);
356dc13fee6SSepherosa Ziehau static int			hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
3576c1204dfSSepherosa Ziehau static int			hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
35840d60d6eSDexuan Cui static int			hn_vf_sysctl(SYSCTL_HANDLER_ARGS);
359499c3e17SSepherosa Ziehau static int			hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS);
360499c3e17SSepherosa Ziehau static int			hn_vflist_sysctl(SYSCTL_HANDLER_ARGS);
361499c3e17SSepherosa Ziehau static int			hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS);
3629c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS);
3639c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS);
36415516c77SSepherosa Ziehau 
3655bdfd3fdSDexuan Cui static void			hn_stop(struct hn_softc *, bool);
36615516c77SSepherosa Ziehau static void			hn_init_locked(struct hn_softc *);
36715516c77SSepherosa Ziehau static int			hn_chan_attach(struct hn_softc *,
36815516c77SSepherosa Ziehau 				    struct vmbus_channel *);
36915516c77SSepherosa Ziehau static void			hn_chan_detach(struct hn_softc *,
37015516c77SSepherosa Ziehau 				    struct vmbus_channel *);
37115516c77SSepherosa Ziehau static int			hn_attach_subchans(struct hn_softc *);
37215516c77SSepherosa Ziehau static void			hn_detach_allchans(struct hn_softc *);
37315516c77SSepherosa Ziehau static void			hn_chan_rollup(struct hn_rx_ring *,
37415516c77SSepherosa Ziehau 				    struct hn_tx_ring *);
37515516c77SSepherosa Ziehau static void			hn_set_ring_inuse(struct hn_softc *, int);
37615516c77SSepherosa Ziehau static int			hn_synth_attach(struct hn_softc *, int);
37715516c77SSepherosa Ziehau static void			hn_synth_detach(struct hn_softc *);
37815516c77SSepherosa Ziehau static int			hn_synth_alloc_subchans(struct hn_softc *,
37915516c77SSepherosa Ziehau 				    int *);
3802494d735SSepherosa Ziehau static bool			hn_synth_attachable(const struct hn_softc *);
38115516c77SSepherosa Ziehau static void			hn_suspend(struct hn_softc *);
38215516c77SSepherosa Ziehau static void			hn_suspend_data(struct hn_softc *);
38315516c77SSepherosa Ziehau static void			hn_suspend_mgmt(struct hn_softc *);
38415516c77SSepherosa Ziehau static void			hn_resume(struct hn_softc *);
38515516c77SSepherosa Ziehau static void			hn_resume_data(struct hn_softc *);
38615516c77SSepherosa Ziehau static void			hn_resume_mgmt(struct hn_softc *);
38715516c77SSepherosa Ziehau static void			hn_suspend_mgmt_taskfunc(void *, int);
38825641fc7SSepherosa Ziehau static void			hn_chan_drain(struct hn_softc *,
38925641fc7SSepherosa Ziehau 				    struct vmbus_channel *);
390b3b75d9cSSepherosa Ziehau static void			hn_disable_rx(struct hn_softc *);
391b3b75d9cSSepherosa Ziehau static void			hn_drain_rxtx(struct hn_softc *, int);
3926c1204dfSSepherosa Ziehau static void			hn_polling(struct hn_softc *, u_int);
3936c1204dfSSepherosa Ziehau static void			hn_chan_polling(struct vmbus_channel *, u_int);
3949c6cae24SSepherosa Ziehau static void			hn_mtu_change_fixup(struct hn_softc *);
39515516c77SSepherosa Ziehau 
39615516c77SSepherosa Ziehau static void			hn_update_link_status(struct hn_softc *);
39715516c77SSepherosa Ziehau static void			hn_change_network(struct hn_softc *);
39815516c77SSepherosa Ziehau static void			hn_link_taskfunc(void *, int);
39915516c77SSepherosa Ziehau static void			hn_netchg_init_taskfunc(void *, int);
40015516c77SSepherosa Ziehau static void			hn_netchg_status_taskfunc(void *, int);
40115516c77SSepherosa Ziehau static void			hn_link_status(struct hn_softc *);
40215516c77SSepherosa Ziehau 
40315516c77SSepherosa Ziehau static int			hn_create_rx_data(struct hn_softc *, int);
40415516c77SSepherosa Ziehau static void			hn_destroy_rx_data(struct hn_softc *);
40515516c77SSepherosa Ziehau static int			hn_check_iplen(const struct mbuf *, int);
406db76829bSSepherosa Ziehau static void			hn_rxpkt_proto(const struct mbuf *, int *, int *);
407f1b0a43fSSepherosa Ziehau static int			hn_set_rxfilter(struct hn_softc *, uint32_t);
408c08f7b2cSSepherosa Ziehau static int			hn_rxfilter_config(struct hn_softc *);
40915516c77SSepherosa Ziehau static int			hn_rss_reconfig(struct hn_softc *);
410afd4971bSSepherosa Ziehau static void			hn_rss_ind_fixup(struct hn_softc *);
411642ec226SSepherosa Ziehau static void			hn_rss_mbuf_hash(struct hn_softc *, uint32_t);
412a491581fSWei Hu static int			hn_rxpkt(struct hn_rx_ring *);
413642ec226SSepherosa Ziehau static uint32_t			hn_rss_type_fromndis(uint32_t);
414642ec226SSepherosa Ziehau static uint32_t			hn_rss_type_tondis(uint32_t);
41515516c77SSepherosa Ziehau 
41615516c77SSepherosa Ziehau static int			hn_tx_ring_create(struct hn_softc *, int);
41715516c77SSepherosa Ziehau static void			hn_tx_ring_destroy(struct hn_tx_ring *);
41815516c77SSepherosa Ziehau static int			hn_create_tx_data(struct hn_softc *, int);
41915516c77SSepherosa Ziehau static void			hn_fixup_tx_data(struct hn_softc *);
420db76829bSSepherosa Ziehau static void			hn_fixup_rx_data(struct hn_softc *);
42115516c77SSepherosa Ziehau static void			hn_destroy_tx_data(struct hn_softc *);
42215516c77SSepherosa Ziehau static void			hn_txdesc_dmamap_destroy(struct hn_txdesc *);
42325641fc7SSepherosa Ziehau static void			hn_txdesc_gc(struct hn_tx_ring *,
42425641fc7SSepherosa Ziehau 				    struct hn_txdesc *);
425dc13fee6SSepherosa Ziehau static int			hn_encap(struct ifnet *, struct hn_tx_ring *,
42615516c77SSepherosa Ziehau 				    struct hn_txdesc *, struct mbuf **);
42715516c77SSepherosa Ziehau static int			hn_txpkt(struct ifnet *, struct hn_tx_ring *,
42815516c77SSepherosa Ziehau 				    struct hn_txdesc *);
42915516c77SSepherosa Ziehau static void			hn_set_chim_size(struct hn_softc *, int);
43015516c77SSepherosa Ziehau static void			hn_set_tso_maxsize(struct hn_softc *, int, int);
43115516c77SSepherosa Ziehau static bool			hn_tx_ring_pending(struct hn_tx_ring *);
43215516c77SSepherosa Ziehau static void			hn_tx_ring_qflush(struct hn_tx_ring *);
43315516c77SSepherosa Ziehau static void			hn_resume_tx(struct hn_softc *, int);
434dc13fee6SSepherosa Ziehau static void			hn_set_txagg(struct hn_softc *);
435dc13fee6SSepherosa Ziehau static void			*hn_try_txagg(struct ifnet *,
436dc13fee6SSepherosa Ziehau 				    struct hn_tx_ring *, struct hn_txdesc *,
437dc13fee6SSepherosa Ziehau 				    int);
43815516c77SSepherosa Ziehau static int			hn_get_txswq_depth(const struct hn_tx_ring *);
43915516c77SSepherosa Ziehau static void			hn_txpkt_done(struct hn_nvs_sendctx *,
44015516c77SSepherosa Ziehau 				    struct hn_softc *, struct vmbus_channel *,
44115516c77SSepherosa Ziehau 				    const void *, int);
44215516c77SSepherosa Ziehau static int			hn_txpkt_sglist(struct hn_tx_ring *,
44315516c77SSepherosa Ziehau 				    struct hn_txdesc *);
44415516c77SSepherosa Ziehau static int			hn_txpkt_chim(struct hn_tx_ring *,
44515516c77SSepherosa Ziehau 				    struct hn_txdesc *);
44615516c77SSepherosa Ziehau static int			hn_xmit(struct hn_tx_ring *, int);
44715516c77SSepherosa Ziehau static void			hn_xmit_taskfunc(void *, int);
44815516c77SSepherosa Ziehau static void			hn_xmit_txeof(struct hn_tx_ring *);
44915516c77SSepherosa Ziehau static void			hn_xmit_txeof_taskfunc(void *, int);
45023bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
45115516c77SSepherosa Ziehau static int			hn_start_locked(struct hn_tx_ring *, int);
45215516c77SSepherosa Ziehau static void			hn_start_taskfunc(void *, int);
45315516c77SSepherosa Ziehau static void			hn_start_txeof(struct hn_tx_ring *);
45415516c77SSepherosa Ziehau static void			hn_start_txeof_taskfunc(void *, int);
45523bf9e15SSepherosa Ziehau #endif
45615516c77SSepherosa Ziehau 
45715516c77SSepherosa Ziehau SYSCTL_NODE(_hw, OID_AUTO, hn, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
45815516c77SSepherosa Ziehau     "Hyper-V network interface");
45915516c77SSepherosa Ziehau 
460*b15a632cSGordon Bergling /* Trust tcp segment verification on host side. */
46115516c77SSepherosa Ziehau static int			hn_trust_hosttcp = 1;
46215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hosttcp, CTLFLAG_RDTUN,
46315516c77SSepherosa Ziehau     &hn_trust_hosttcp, 0,
464*b15a632cSGordon Bergling     "Trust tcp segment verification on host side, "
46515516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
46615516c77SSepherosa Ziehau 
46715516c77SSepherosa Ziehau /* Trust udp datagrams verification on host side. */
46815516c77SSepherosa Ziehau static int			hn_trust_hostudp = 1;
46915516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostudp, CTLFLAG_RDTUN,
47015516c77SSepherosa Ziehau     &hn_trust_hostudp, 0,
47115516c77SSepherosa Ziehau     "Trust udp datagram verification on host side, "
47215516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
47315516c77SSepherosa Ziehau 
47415516c77SSepherosa Ziehau /* Trust ip packets verification on host side. */
47515516c77SSepherosa Ziehau static int			hn_trust_hostip = 1;
47615516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostip, CTLFLAG_RDTUN,
47715516c77SSepherosa Ziehau     &hn_trust_hostip, 0,
47815516c77SSepherosa Ziehau     "Trust ip packet verification on host side, "
47915516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
48015516c77SSepherosa Ziehau 
4812be266caSSepherosa Ziehau /*
4822be266caSSepherosa Ziehau  * Offload UDP/IPv4 checksum.
4832be266caSSepherosa Ziehau  */
4842be266caSSepherosa Ziehau static int			hn_enable_udp4cs = 1;
4852be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp4cs, CTLFLAG_RDTUN,
4862be266caSSepherosa Ziehau     &hn_enable_udp4cs, 0, "Offload UDP/IPv4 checksum");
4872be266caSSepherosa Ziehau 
4882be266caSSepherosa Ziehau /*
4892be266caSSepherosa Ziehau  * Offload UDP/IPv6 checksum.
4902be266caSSepherosa Ziehau  */
4912be266caSSepherosa Ziehau static int			hn_enable_udp6cs = 1;
4922be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp6cs, CTLFLAG_RDTUN,
4932be266caSSepherosa Ziehau     &hn_enable_udp6cs, 0, "Offload UDP/IPv6 checksum");
4942be266caSSepherosa Ziehau 
4952be266caSSepherosa Ziehau /* Stats. */
4962be266caSSepherosa Ziehau static counter_u64_t		hn_udpcs_fixup;
4972be266caSSepherosa Ziehau SYSCTL_COUNTER_U64(_hw_hn, OID_AUTO, udpcs_fixup, CTLFLAG_RW,
4982be266caSSepherosa Ziehau     &hn_udpcs_fixup, "# of UDP checksum fixup");
4992be266caSSepherosa Ziehau 
5002be266caSSepherosa Ziehau /*
5012be266caSSepherosa Ziehau  * See hn_set_hlen().
5022be266caSSepherosa Ziehau  *
5032be266caSSepherosa Ziehau  * This value is for Azure.  For Hyper-V, set this above
5042be266caSSepherosa Ziehau  * 65536 to disable UDP datagram checksum fixup.
5052be266caSSepherosa Ziehau  */
5062be266caSSepherosa Ziehau static int			hn_udpcs_fixup_mtu = 1420;
5072be266caSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, udpcs_fixup_mtu, CTLFLAG_RWTUN,
5082be266caSSepherosa Ziehau     &hn_udpcs_fixup_mtu, 0, "UDP checksum fixup MTU threshold");
5092be266caSSepherosa Ziehau 
51015516c77SSepherosa Ziehau /* Limit TSO burst size */
51115516c77SSepherosa Ziehau static int			hn_tso_maxlen = IP_MAXPACKET;
51215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tso_maxlen, CTLFLAG_RDTUN,
51315516c77SSepherosa Ziehau     &hn_tso_maxlen, 0, "TSO burst limit");
51415516c77SSepherosa Ziehau 
51515516c77SSepherosa Ziehau /* Limit chimney send size */
51615516c77SSepherosa Ziehau static int			hn_tx_chimney_size = 0;
51715516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_chimney_size, CTLFLAG_RDTUN,
51815516c77SSepherosa Ziehau     &hn_tx_chimney_size, 0, "Chimney send packet size limit");
51915516c77SSepherosa Ziehau 
52015516c77SSepherosa Ziehau /* Limit the size of packet for direct transmission */
52115516c77SSepherosa Ziehau static int			hn_direct_tx_size = HN_DIRECT_TX_SIZE_DEF;
52215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, direct_tx_size, CTLFLAG_RDTUN,
52315516c77SSepherosa Ziehau     &hn_direct_tx_size, 0, "Size of the packet for direct transmission");
52415516c77SSepherosa Ziehau 
52515516c77SSepherosa Ziehau /* # of LRO entries per RX ring */
52615516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
52715516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
52815516c77SSepherosa Ziehau static int			hn_lro_entry_count = HN_LROENT_CNT_DEF;
52915516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, lro_entry_count, CTLFLAG_RDTUN,
53015516c77SSepherosa Ziehau     &hn_lro_entry_count, 0, "LRO entry count");
53115516c77SSepherosa Ziehau #endif
53215516c77SSepherosa Ziehau #endif
53315516c77SSepherosa Ziehau 
534fdd0222aSSepherosa Ziehau static int			hn_tx_taskq_cnt = 1;
535fdd0222aSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_cnt, CTLFLAG_RDTUN,
536fdd0222aSSepherosa Ziehau     &hn_tx_taskq_cnt, 0, "# of TX taskqueues");
537fdd0222aSSepherosa Ziehau 
5380e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_INDEP	0
5390e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_GLOBAL	1
5400e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_EVTTQ	2
5410e11868dSSepherosa Ziehau 
5420e11868dSSepherosa Ziehau static int			hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
5430e11868dSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_mode, CTLFLAG_RDTUN,
5440e11868dSSepherosa Ziehau     &hn_tx_taskq_mode, 0, "TX taskqueue modes: "
5450e11868dSSepherosa Ziehau     "0 - independent, 1 - share global tx taskqs, 2 - share event taskqs");
5460e11868dSSepherosa Ziehau 
54715516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
54815516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 0;
54915516c77SSepherosa Ziehau #else
55015516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 1;
55115516c77SSepherosa Ziehau #endif
55215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_txdesc_bufring, CTLFLAG_RD,
55315516c77SSepherosa Ziehau     &hn_use_txdesc_bufring, 0, "Use buf_ring for TX descriptors");
55415516c77SSepherosa Ziehau 
55523bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
55615516c77SSepherosa Ziehau /* Use ifnet.if_start instead of ifnet.if_transmit */
55715516c77SSepherosa Ziehau static int			hn_use_if_start = 0;
55815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_if_start, CTLFLAG_RDTUN,
55915516c77SSepherosa Ziehau     &hn_use_if_start, 0, "Use if_start TX method");
56023bf9e15SSepherosa Ziehau #endif
56115516c77SSepherosa Ziehau 
56215516c77SSepherosa Ziehau /* # of channels to use */
56315516c77SSepherosa Ziehau static int			hn_chan_cnt = 0;
56415516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, chan_cnt, CTLFLAG_RDTUN,
56515516c77SSepherosa Ziehau     &hn_chan_cnt, 0,
56615516c77SSepherosa Ziehau     "# of channels to use; each channel has one RX ring and one TX ring");
56715516c77SSepherosa Ziehau 
56815516c77SSepherosa Ziehau /* # of transmit rings to use */
56915516c77SSepherosa Ziehau static int			hn_tx_ring_cnt = 0;
57015516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_ring_cnt, CTLFLAG_RDTUN,
57115516c77SSepherosa Ziehau     &hn_tx_ring_cnt, 0, "# of TX rings to use");
57215516c77SSepherosa Ziehau 
57315516c77SSepherosa Ziehau /* Software TX ring deptch */
57415516c77SSepherosa Ziehau static int			hn_tx_swq_depth = 0;
57515516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_swq_depth, CTLFLAG_RDTUN,
57615516c77SSepherosa Ziehau     &hn_tx_swq_depth, 0, "Depth of IFQ or BUFRING");
57715516c77SSepherosa Ziehau 
57815516c77SSepherosa Ziehau /* Enable sorted LRO, and the depth of the per-channel mbuf queue */
57915516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
58015516c77SSepherosa Ziehau static u_int			hn_lro_mbufq_depth = 0;
58115516c77SSepherosa Ziehau SYSCTL_UINT(_hw_hn, OID_AUTO, lro_mbufq_depth, CTLFLAG_RDTUN,
58215516c77SSepherosa Ziehau     &hn_lro_mbufq_depth, 0, "Depth of LRO mbuf queue");
58315516c77SSepherosa Ziehau #endif
58415516c77SSepherosa Ziehau 
585dc13fee6SSepherosa Ziehau /* Packet transmission aggregation size limit */
586dc13fee6SSepherosa Ziehau static int			hn_tx_agg_size = -1;
587dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_size, CTLFLAG_RDTUN,
588dc13fee6SSepherosa Ziehau     &hn_tx_agg_size, 0, "Packet transmission aggregation size limit");
589dc13fee6SSepherosa Ziehau 
590dc13fee6SSepherosa Ziehau /* Packet transmission aggregation count limit */
591fa915c4dSSepherosa Ziehau static int			hn_tx_agg_pkts = -1;
592dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_pkts, CTLFLAG_RDTUN,
593dc13fee6SSepherosa Ziehau     &hn_tx_agg_pkts, 0, "Packet transmission aggregation packet limit");
594dc13fee6SSepherosa Ziehau 
595499c3e17SSepherosa Ziehau /* VF list */
5967029da5cSPawel Biernacki SYSCTL_PROC(_hw_hn, OID_AUTO, vflist,
5977029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 0, 0,
5987029da5cSPawel Biernacki     hn_vflist_sysctl, "A",
5997029da5cSPawel Biernacki     "VF list");
600499c3e17SSepherosa Ziehau 
601499c3e17SSepherosa Ziehau /* VF mapping */
6027029da5cSPawel Biernacki SYSCTL_PROC(_hw_hn, OID_AUTO, vfmap,
6037029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, 0, 0,
6047029da5cSPawel Biernacki     hn_vfmap_sysctl, "A",
6057029da5cSPawel Biernacki     "VF mapping");
606499c3e17SSepherosa Ziehau 
6079c6cae24SSepherosa Ziehau /* Transparent VF */
60878e46963SSepherosa Ziehau static int			hn_xpnt_vf = 1;
6099c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_transparent, CTLFLAG_RDTUN,
6109c6cae24SSepherosa Ziehau     &hn_xpnt_vf, 0, "Transparent VF mod");
6119c6cae24SSepherosa Ziehau 
6129c6cae24SSepherosa Ziehau /* Accurate BPF support for Transparent VF */
6139c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf = 0;
6149c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_accbpf, CTLFLAG_RDTUN,
6159c6cae24SSepherosa Ziehau     &hn_xpnt_vf_accbpf, 0, "Accurate BPF for transparent VF");
6169c6cae24SSepherosa Ziehau 
6179c6cae24SSepherosa Ziehau /* Extra wait for transparent VF attach routing; unit seconds. */
6189c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
6199c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_attwait, CTLFLAG_RWTUN,
6209c6cae24SSepherosa Ziehau     &hn_xpnt_vf_attwait, 0,
6219c6cae24SSepherosa Ziehau     "Extra wait for transparent VF attach routing; unit: seconds");
6229c6cae24SSepherosa Ziehau 
62315516c77SSepherosa Ziehau static u_int			hn_cpu_index;	/* next CPU for channel */
624fdd0222aSSepherosa Ziehau static struct taskqueue		**hn_tx_taskque;/* shared TX taskqueues */
62515516c77SSepherosa Ziehau 
626499c3e17SSepherosa Ziehau static struct rmlock		hn_vfmap_lock;
627499c3e17SSepherosa Ziehau static int			hn_vfmap_size;
628499c3e17SSepherosa Ziehau static struct ifnet		**hn_vfmap;
629499c3e17SSepherosa Ziehau 
63034d68912SSepherosa Ziehau #ifndef RSS
63115516c77SSepherosa Ziehau static const uint8_t
63215516c77SSepherosa Ziehau hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
63315516c77SSepherosa Ziehau 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
63415516c77SSepherosa Ziehau 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
63515516c77SSepherosa Ziehau 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
63615516c77SSepherosa Ziehau 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
63715516c77SSepherosa Ziehau 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
63815516c77SSepherosa Ziehau };
63934d68912SSepherosa Ziehau #endif	/* !RSS */
64015516c77SSepherosa Ziehau 
641c2d50b26SSepherosa Ziehau static const struct hyperv_guid	hn_guid = {
642c2d50b26SSepherosa Ziehau 	.hv_guid = {
643c2d50b26SSepherosa Ziehau 	    0x63, 0x51, 0x61, 0xf8, 0x3e, 0xdf, 0xc5, 0x46,
644c2d50b26SSepherosa Ziehau 	    0x91, 0x3f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e }
645c2d50b26SSepherosa Ziehau };
646c2d50b26SSepherosa Ziehau 
64715516c77SSepherosa Ziehau static device_method_t hn_methods[] = {
64815516c77SSepherosa Ziehau 	/* Device interface */
64915516c77SSepherosa Ziehau 	DEVMETHOD(device_probe,		hn_probe),
65015516c77SSepherosa Ziehau 	DEVMETHOD(device_attach,	hn_attach),
65115516c77SSepherosa Ziehau 	DEVMETHOD(device_detach,	hn_detach),
65215516c77SSepherosa Ziehau 	DEVMETHOD(device_shutdown,	hn_shutdown),
65315516c77SSepherosa Ziehau 	DEVMETHOD_END
65415516c77SSepherosa Ziehau };
65515516c77SSepherosa Ziehau 
65615516c77SSepherosa Ziehau static driver_t hn_driver = {
65715516c77SSepherosa Ziehau 	"hn",
65815516c77SSepherosa Ziehau 	hn_methods,
65915516c77SSepherosa Ziehau 	sizeof(struct hn_softc)
66015516c77SSepherosa Ziehau };
66115516c77SSepherosa Ziehau 
66215516c77SSepherosa Ziehau static devclass_t hn_devclass;
66315516c77SSepherosa Ziehau 
66415516c77SSepherosa Ziehau DRIVER_MODULE(hn, vmbus, hn_driver, hn_devclass, 0, 0);
66515516c77SSepherosa Ziehau MODULE_VERSION(hn, 1);
66615516c77SSepherosa Ziehau MODULE_DEPEND(hn, vmbus, 1, 1, 1);
66715516c77SSepherosa Ziehau 
66815516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
66915516c77SSepherosa Ziehau static void
67015516c77SSepherosa Ziehau hn_set_lro_lenlim(struct hn_softc *sc, int lenlim)
67115516c77SSepherosa Ziehau {
67215516c77SSepherosa Ziehau 	int i;
67315516c77SSepherosa Ziehau 
674a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
67515516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_length_lim = lenlim;
67615516c77SSepherosa Ziehau }
67715516c77SSepherosa Ziehau #endif
67815516c77SSepherosa Ziehau 
67915516c77SSepherosa Ziehau static int
68015516c77SSepherosa Ziehau hn_txpkt_sglist(struct hn_tx_ring *txr, struct hn_txdesc *txd)
68115516c77SSepherosa Ziehau {
68215516c77SSepherosa Ziehau 
68315516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
68415516c77SSepherosa Ziehau 	    txd->chim_size == 0, ("invalid rndis sglist txd"));
68515516c77SSepherosa Ziehau 	return (hn_nvs_send_rndis_sglist(txr->hn_chan, HN_NVS_RNDIS_MTYPE_DATA,
68615516c77SSepherosa Ziehau 	    &txd->send_ctx, txr->hn_gpa, txr->hn_gpa_cnt));
68715516c77SSepherosa Ziehau }
68815516c77SSepherosa Ziehau 
68915516c77SSepherosa Ziehau static int
69015516c77SSepherosa Ziehau hn_txpkt_chim(struct hn_tx_ring *txr, struct hn_txdesc *txd)
69115516c77SSepherosa Ziehau {
69215516c77SSepherosa Ziehau 	struct hn_nvs_rndis rndis;
69315516c77SSepherosa Ziehau 
69415516c77SSepherosa Ziehau 	KASSERT(txd->chim_index != HN_NVS_CHIM_IDX_INVALID &&
69515516c77SSepherosa Ziehau 	    txd->chim_size > 0, ("invalid rndis chim txd"));
69615516c77SSepherosa Ziehau 
69715516c77SSepherosa Ziehau 	rndis.nvs_type = HN_NVS_TYPE_RNDIS;
69815516c77SSepherosa Ziehau 	rndis.nvs_rndis_mtype = HN_NVS_RNDIS_MTYPE_DATA;
69915516c77SSepherosa Ziehau 	rndis.nvs_chim_idx = txd->chim_index;
70015516c77SSepherosa Ziehau 	rndis.nvs_chim_sz = txd->chim_size;
70115516c77SSepherosa Ziehau 
70215516c77SSepherosa Ziehau 	return (hn_nvs_send(txr->hn_chan, VMBUS_CHANPKT_FLAG_RC,
70315516c77SSepherosa Ziehau 	    &rndis, sizeof(rndis), &txd->send_ctx));
70415516c77SSepherosa Ziehau }
70515516c77SSepherosa Ziehau 
70615516c77SSepherosa Ziehau static __inline uint32_t
70715516c77SSepherosa Ziehau hn_chim_alloc(struct hn_softc *sc)
70815516c77SSepherosa Ziehau {
70915516c77SSepherosa Ziehau 	int i, bmap_cnt = sc->hn_chim_bmap_cnt;
71015516c77SSepherosa Ziehau 	u_long *bmap = sc->hn_chim_bmap;
71115516c77SSepherosa Ziehau 	uint32_t ret = HN_NVS_CHIM_IDX_INVALID;
71215516c77SSepherosa Ziehau 
71315516c77SSepherosa Ziehau 	for (i = 0; i < bmap_cnt; ++i) {
71415516c77SSepherosa Ziehau 		int idx;
71515516c77SSepherosa Ziehau 
71615516c77SSepherosa Ziehau 		idx = ffsl(~bmap[i]);
71715516c77SSepherosa Ziehau 		if (idx == 0)
71815516c77SSepherosa Ziehau 			continue;
71915516c77SSepherosa Ziehau 
72015516c77SSepherosa Ziehau 		--idx; /* ffsl is 1-based */
72115516c77SSepherosa Ziehau 		KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt,
72215516c77SSepherosa Ziehau 		    ("invalid i %d and idx %d", i, idx));
72315516c77SSepherosa Ziehau 
72415516c77SSepherosa Ziehau 		if (atomic_testandset_long(&bmap[i], idx))
72515516c77SSepherosa Ziehau 			continue;
72615516c77SSepherosa Ziehau 
72715516c77SSepherosa Ziehau 		ret = i * LONG_BIT + idx;
72815516c77SSepherosa Ziehau 		break;
72915516c77SSepherosa Ziehau 	}
73015516c77SSepherosa Ziehau 	return (ret);
73115516c77SSepherosa Ziehau }
73215516c77SSepherosa Ziehau 
73315516c77SSepherosa Ziehau static __inline void
73415516c77SSepherosa Ziehau hn_chim_free(struct hn_softc *sc, uint32_t chim_idx)
73515516c77SSepherosa Ziehau {
73615516c77SSepherosa Ziehau 	u_long mask;
73715516c77SSepherosa Ziehau 	uint32_t idx;
73815516c77SSepherosa Ziehau 
73915516c77SSepherosa Ziehau 	idx = chim_idx / LONG_BIT;
74015516c77SSepherosa Ziehau 	KASSERT(idx < sc->hn_chim_bmap_cnt,
74115516c77SSepherosa Ziehau 	    ("invalid chimney index 0x%x", chim_idx));
74215516c77SSepherosa Ziehau 
74315516c77SSepherosa Ziehau 	mask = 1UL << (chim_idx % LONG_BIT);
74415516c77SSepherosa Ziehau 	KASSERT(sc->hn_chim_bmap[idx] & mask,
74515516c77SSepherosa Ziehau 	    ("index bitmap 0x%lx, chimney index %u, "
74615516c77SSepherosa Ziehau 	     "bitmap idx %d, bitmask 0x%lx",
74715516c77SSepherosa Ziehau 	     sc->hn_chim_bmap[idx], chim_idx, idx, mask));
74815516c77SSepherosa Ziehau 
74915516c77SSepherosa Ziehau 	atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
75015516c77SSepherosa Ziehau }
75115516c77SSepherosa Ziehau 
752edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
753cc0c6ebcSSepherosa Ziehau 
754cc0c6ebcSSepherosa Ziehau #define PULLUP_HDR(m, len)				\
755cc0c6ebcSSepherosa Ziehau do {							\
756cc0c6ebcSSepherosa Ziehau 	if (__predict_false((m)->m_len < (len))) {	\
757cc0c6ebcSSepherosa Ziehau 		(m) = m_pullup((m), (len));		\
758cc0c6ebcSSepherosa Ziehau 		if ((m) == NULL)			\
759cc0c6ebcSSepherosa Ziehau 			return (NULL);			\
760cc0c6ebcSSepherosa Ziehau 	}						\
761cc0c6ebcSSepherosa Ziehau } while (0)
762cc0c6ebcSSepherosa Ziehau 
763edd3f315SSepherosa Ziehau /*
764edd3f315SSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
765edd3f315SSepherosa Ziehau  */
766edd3f315SSepherosa Ziehau static __inline struct mbuf *
767edd3f315SSepherosa Ziehau hn_tso_fixup(struct mbuf *m_head)
768edd3f315SSepherosa Ziehau {
769edd3f315SSepherosa Ziehau 	struct ether_vlan_header *evl;
770edd3f315SSepherosa Ziehau 	struct tcphdr *th;
771edd3f315SSepherosa Ziehau 	int ehlen;
772edd3f315SSepherosa Ziehau 
773edd3f315SSepherosa Ziehau 	KASSERT(M_WRITABLE(m_head), ("TSO mbuf not writable"));
774edd3f315SSepherosa Ziehau 
775edd3f315SSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
776edd3f315SSepherosa Ziehau 	evl = mtod(m_head, struct ether_vlan_header *);
777edd3f315SSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
778edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
779edd3f315SSepherosa Ziehau 	else
780edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
781c49d47daSSepherosa Ziehau 	m_head->m_pkthdr.l2hlen = ehlen;
782edd3f315SSepherosa Ziehau 
783edd3f315SSepherosa Ziehau #ifdef INET
784edd3f315SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
785edd3f315SSepherosa Ziehau 		struct ip *ip;
786edd3f315SSepherosa Ziehau 		int iphlen;
787edd3f315SSepherosa Ziehau 
788edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
789edd3f315SSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
790edd3f315SSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
791c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = iphlen;
792edd3f315SSepherosa Ziehau 
793edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
794edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + iphlen);
795edd3f315SSepherosa Ziehau 
796edd3f315SSepherosa Ziehau 		ip->ip_len = 0;
797edd3f315SSepherosa Ziehau 		ip->ip_sum = 0;
798edd3f315SSepherosa Ziehau 		th->th_sum = in_pseudo(ip->ip_src.s_addr,
799edd3f315SSepherosa Ziehau 		    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
800edd3f315SSepherosa Ziehau 	}
801edd3f315SSepherosa Ziehau #endif
802edd3f315SSepherosa Ziehau #if defined(INET6) && defined(INET)
803edd3f315SSepherosa Ziehau 	else
804edd3f315SSepherosa Ziehau #endif
805edd3f315SSepherosa Ziehau #ifdef INET6
806edd3f315SSepherosa Ziehau 	{
807edd3f315SSepherosa Ziehau 		struct ip6_hdr *ip6;
808edd3f315SSepherosa Ziehau 
809edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
810edd3f315SSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
811edd3f315SSepherosa Ziehau 		if (ip6->ip6_nxt != IPPROTO_TCP) {
812edd3f315SSepherosa Ziehau 			m_freem(m_head);
813edd3f315SSepherosa Ziehau 			return (NULL);
814edd3f315SSepherosa Ziehau 		}
815c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = sizeof(*ip6);
816edd3f315SSepherosa Ziehau 
817edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
818edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + sizeof(*ip6));
819edd3f315SSepherosa Ziehau 
820edd3f315SSepherosa Ziehau 		ip6->ip6_plen = 0;
821edd3f315SSepherosa Ziehau 		th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
822edd3f315SSepherosa Ziehau 	}
823edd3f315SSepherosa Ziehau #endif
824edd3f315SSepherosa Ziehau 	return (m_head);
825edd3f315SSepherosa Ziehau }
826cc0c6ebcSSepherosa Ziehau 
827cc0c6ebcSSepherosa Ziehau /*
828cc0c6ebcSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
829cc0c6ebcSSepherosa Ziehau  */
830cc0c6ebcSSepherosa Ziehau static __inline struct mbuf *
831c49d47daSSepherosa Ziehau hn_set_hlen(struct mbuf *m_head)
832cc0c6ebcSSepherosa Ziehau {
833cc0c6ebcSSepherosa Ziehau 	const struct ether_vlan_header *evl;
834cc0c6ebcSSepherosa Ziehau 	int ehlen;
835cc0c6ebcSSepherosa Ziehau 
836cc0c6ebcSSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
837cc0c6ebcSSepherosa Ziehau 	evl = mtod(m_head, const struct ether_vlan_header *);
838cc0c6ebcSSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
839cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
840cc0c6ebcSSepherosa Ziehau 	else
841cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
842c49d47daSSepherosa Ziehau 	m_head->m_pkthdr.l2hlen = ehlen;
843cc0c6ebcSSepherosa Ziehau 
844cc0c6ebcSSepherosa Ziehau #ifdef INET
845c49d47daSSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP_UDP)) {
846cc0c6ebcSSepherosa Ziehau 		const struct ip *ip;
847cc0c6ebcSSepherosa Ziehau 		int iphlen;
848cc0c6ebcSSepherosa Ziehau 
849cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
850cc0c6ebcSSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
851cc0c6ebcSSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
852c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = iphlen;
8532be266caSSepherosa Ziehau 
8542be266caSSepherosa Ziehau 		/*
8552be266caSSepherosa Ziehau 		 * UDP checksum offload does not work in Azure, if the
8562be266caSSepherosa Ziehau 		 * following conditions meet:
8572be266caSSepherosa Ziehau 		 * - sizeof(IP hdr + UDP hdr + payload) > 1420.
8582be266caSSepherosa Ziehau 		 * - IP_DF is not set in the IP hdr.
8592be266caSSepherosa Ziehau 		 *
8602be266caSSepherosa Ziehau 		 * Fallback to software checksum for these UDP datagrams.
8612be266caSSepherosa Ziehau 		 */
8622be266caSSepherosa Ziehau 		if ((m_head->m_pkthdr.csum_flags & CSUM_IP_UDP) &&
8632be266caSSepherosa Ziehau 		    m_head->m_pkthdr.len > hn_udpcs_fixup_mtu + ehlen &&
8642be266caSSepherosa Ziehau 		    (ntohs(ip->ip_off) & IP_DF) == 0) {
8652be266caSSepherosa Ziehau 			uint16_t off = ehlen + iphlen;
8662be266caSSepherosa Ziehau 
8672be266caSSepherosa Ziehau 			counter_u64_add(hn_udpcs_fixup, 1);
8682be266caSSepherosa Ziehau 			PULLUP_HDR(m_head, off + sizeof(struct udphdr));
8692be266caSSepherosa Ziehau 			*(uint16_t *)(m_head->m_data + off +
8702be266caSSepherosa Ziehau                             m_head->m_pkthdr.csum_data) = in_cksum_skip(
8712be266caSSepherosa Ziehau 			    m_head, m_head->m_pkthdr.len, off);
8722be266caSSepherosa Ziehau 			m_head->m_pkthdr.csum_flags &= ~CSUM_IP_UDP;
8732be266caSSepherosa Ziehau 		}
874cc0c6ebcSSepherosa Ziehau 	}
875cc0c6ebcSSepherosa Ziehau #endif
876cc0c6ebcSSepherosa Ziehau #if defined(INET6) && defined(INET)
877cc0c6ebcSSepherosa Ziehau 	else
878cc0c6ebcSSepherosa Ziehau #endif
879cc0c6ebcSSepherosa Ziehau #ifdef INET6
880cc0c6ebcSSepherosa Ziehau 	{
881cc0c6ebcSSepherosa Ziehau 		const struct ip6_hdr *ip6;
882cc0c6ebcSSepherosa Ziehau 
883cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
884cc0c6ebcSSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
885f0319886SWei Hu 		if (ip6->ip6_nxt != IPPROTO_TCP &&
886f0319886SWei Hu 		    ip6->ip6_nxt != IPPROTO_UDP) {
887c49d47daSSepherosa Ziehau 			m_freem(m_head);
888c49d47daSSepherosa Ziehau 			return (NULL);
889c49d47daSSepherosa Ziehau 		}
890c49d47daSSepherosa Ziehau 		m_head->m_pkthdr.l3hlen = sizeof(*ip6);
891cc0c6ebcSSepherosa Ziehau 	}
892cc0c6ebcSSepherosa Ziehau #endif
893cc0c6ebcSSepherosa Ziehau 	return (m_head);
894cc0c6ebcSSepherosa Ziehau }
895cc0c6ebcSSepherosa Ziehau 
896c49d47daSSepherosa Ziehau /*
897c49d47daSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
898c49d47daSSepherosa Ziehau  */
899c49d47daSSepherosa Ziehau static __inline struct mbuf *
900c49d47daSSepherosa Ziehau hn_check_tcpsyn(struct mbuf *m_head, int *tcpsyn)
901c49d47daSSepherosa Ziehau {
902c49d47daSSepherosa Ziehau 	const struct tcphdr *th;
903c49d47daSSepherosa Ziehau 	int ehlen, iphlen;
904c49d47daSSepherosa Ziehau 
905c49d47daSSepherosa Ziehau 	*tcpsyn = 0;
906c49d47daSSepherosa Ziehau 	ehlen = m_head->m_pkthdr.l2hlen;
907c49d47daSSepherosa Ziehau 	iphlen = m_head->m_pkthdr.l3hlen;
908c49d47daSSepherosa Ziehau 
909c49d47daSSepherosa Ziehau 	PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
910c49d47daSSepherosa Ziehau 	th = mtodo(m_head, ehlen + iphlen);
911c49d47daSSepherosa Ziehau 	if (th->th_flags & TH_SYN)
912c49d47daSSepherosa Ziehau 		*tcpsyn = 1;
913c49d47daSSepherosa Ziehau 	return (m_head);
914c49d47daSSepherosa Ziehau }
915c49d47daSSepherosa Ziehau 
916cc0c6ebcSSepherosa Ziehau #undef PULLUP_HDR
917cc0c6ebcSSepherosa Ziehau 
918edd3f315SSepherosa Ziehau #endif	/* INET6 || INET */
919edd3f315SSepherosa Ziehau 
92015516c77SSepherosa Ziehau static int
921f1b0a43fSSepherosa Ziehau hn_set_rxfilter(struct hn_softc *sc, uint32_t filter)
922f1b0a43fSSepherosa Ziehau {
923f1b0a43fSSepherosa Ziehau 	int error = 0;
924f1b0a43fSSepherosa Ziehau 
925f1b0a43fSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
926f1b0a43fSSepherosa Ziehau 
927f1b0a43fSSepherosa Ziehau 	if (sc->hn_rx_filter != filter) {
928f1b0a43fSSepherosa Ziehau 		error = hn_rndis_set_rxfilter(sc, filter);
929f1b0a43fSSepherosa Ziehau 		if (!error)
930f1b0a43fSSepherosa Ziehau 			sc->hn_rx_filter = filter;
931f1b0a43fSSepherosa Ziehau 	}
932f1b0a43fSSepherosa Ziehau 	return (error);
933f1b0a43fSSepherosa Ziehau }
934f1b0a43fSSepherosa Ziehau 
935f1b0a43fSSepherosa Ziehau static int
936c08f7b2cSSepherosa Ziehau hn_rxfilter_config(struct hn_softc *sc)
93715516c77SSepherosa Ziehau {
93815516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
93915516c77SSepherosa Ziehau 	uint32_t filter;
94015516c77SSepherosa Ziehau 
94115516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
94215516c77SSepherosa Ziehau 
9439c6cae24SSepherosa Ziehau 	/*
9449c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, we don't know how
9459c6cae24SSepherosa Ziehau 	 * its RX filter is configured, so stick the synthetic device in
9469c6cae24SSepherosa Ziehau 	 * the promiscous mode.
9479c6cae24SSepherosa Ziehau 	 */
9489c6cae24SSepherosa Ziehau 	if ((ifp->if_flags & IFF_PROMISC) || (sc->hn_flags & HN_FLAG_RXVF)) {
94915516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
95015516c77SSepherosa Ziehau 	} else {
95115516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_DIRECTED;
95215516c77SSepherosa Ziehau 		if (ifp->if_flags & IFF_BROADCAST)
95315516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_BROADCAST;
95415516c77SSepherosa Ziehau 		/* TODO: support multicast list */
95515516c77SSepherosa Ziehau 		if ((ifp->if_flags & IFF_ALLMULTI) ||
956d7c5a620SMatt Macy 		    !CK_STAILQ_EMPTY(&ifp->if_multiaddrs))
95715516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
95815516c77SSepherosa Ziehau 	}
959f1b0a43fSSepherosa Ziehau 	return (hn_set_rxfilter(sc, filter));
96015516c77SSepherosa Ziehau }
96115516c77SSepherosa Ziehau 
962dc13fee6SSepherosa Ziehau static void
963dc13fee6SSepherosa Ziehau hn_set_txagg(struct hn_softc *sc)
964dc13fee6SSepherosa Ziehau {
965dc13fee6SSepherosa Ziehau 	uint32_t size, pkts;
966dc13fee6SSepherosa Ziehau 	int i;
967dc13fee6SSepherosa Ziehau 
968dc13fee6SSepherosa Ziehau 	/*
969dc13fee6SSepherosa Ziehau 	 * Setup aggregation size.
970dc13fee6SSepherosa Ziehau 	 */
971dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_size < 0)
972dc13fee6SSepherosa Ziehau 		size = UINT32_MAX;
973dc13fee6SSepherosa Ziehau 	else
974dc13fee6SSepherosa Ziehau 		size = sc->hn_agg_size;
975dc13fee6SSepherosa Ziehau 
976dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_size < size)
977dc13fee6SSepherosa Ziehau 		size = sc->hn_rndis_agg_size;
978dc13fee6SSepherosa Ziehau 
979a4364cfeSSepherosa Ziehau 	/* NOTE: We only aggregate packets using chimney sending buffers. */
980a4364cfeSSepherosa Ziehau 	if (size > (uint32_t)sc->hn_chim_szmax)
981a4364cfeSSepherosa Ziehau 		size = sc->hn_chim_szmax;
982a4364cfeSSepherosa Ziehau 
983dc13fee6SSepherosa Ziehau 	if (size <= 2 * HN_PKTSIZE_MIN(sc->hn_rndis_agg_align)) {
984dc13fee6SSepherosa Ziehau 		/* Disable */
985dc13fee6SSepherosa Ziehau 		size = 0;
986dc13fee6SSepherosa Ziehau 		pkts = 0;
987dc13fee6SSepherosa Ziehau 		goto done;
988dc13fee6SSepherosa Ziehau 	}
989dc13fee6SSepherosa Ziehau 
990dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'int'. */
991dc13fee6SSepherosa Ziehau 	if (size > INT_MAX)
992dc13fee6SSepherosa Ziehau 		size = INT_MAX;
993dc13fee6SSepherosa Ziehau 
994dc13fee6SSepherosa Ziehau 	/*
995dc13fee6SSepherosa Ziehau 	 * Setup aggregation packet count.
996dc13fee6SSepherosa Ziehau 	 */
997dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_pkts < 0)
998dc13fee6SSepherosa Ziehau 		pkts = UINT32_MAX;
999dc13fee6SSepherosa Ziehau 	else
1000dc13fee6SSepherosa Ziehau 		pkts = sc->hn_agg_pkts;
1001dc13fee6SSepherosa Ziehau 
1002dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_pkts < pkts)
1003dc13fee6SSepherosa Ziehau 		pkts = sc->hn_rndis_agg_pkts;
1004dc13fee6SSepherosa Ziehau 
1005dc13fee6SSepherosa Ziehau 	if (pkts <= 1) {
1006dc13fee6SSepherosa Ziehau 		/* Disable */
1007dc13fee6SSepherosa Ziehau 		size = 0;
1008dc13fee6SSepherosa Ziehau 		pkts = 0;
1009dc13fee6SSepherosa Ziehau 		goto done;
1010dc13fee6SSepherosa Ziehau 	}
1011dc13fee6SSepherosa Ziehau 
1012dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
1013dc13fee6SSepherosa Ziehau 	if (pkts > SHRT_MAX)
1014dc13fee6SSepherosa Ziehau 		pkts = SHRT_MAX;
1015dc13fee6SSepherosa Ziehau 
1016dc13fee6SSepherosa Ziehau done:
1017dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
1018dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_align > SHRT_MAX) {
1019dc13fee6SSepherosa Ziehau 		/* Disable */
1020dc13fee6SSepherosa Ziehau 		size = 0;
1021dc13fee6SSepherosa Ziehau 		pkts = 0;
1022dc13fee6SSepherosa Ziehau 	}
1023dc13fee6SSepherosa Ziehau 
1024dc13fee6SSepherosa Ziehau 	if (bootverbose) {
1025dc13fee6SSepherosa Ziehau 		if_printf(sc->hn_ifp, "TX agg size %u, pkts %u, align %u\n",
1026dc13fee6SSepherosa Ziehau 		    size, pkts, sc->hn_rndis_agg_align);
1027dc13fee6SSepherosa Ziehau 	}
1028dc13fee6SSepherosa Ziehau 
1029dc13fee6SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
1030dc13fee6SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
1031dc13fee6SSepherosa Ziehau 
1032dc13fee6SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
1033dc13fee6SSepherosa Ziehau 		txr->hn_agg_szmax = size;
1034dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktmax = pkts;
1035dc13fee6SSepherosa Ziehau 		txr->hn_agg_align = sc->hn_rndis_agg_align;
1036dc13fee6SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
1037dc13fee6SSepherosa Ziehau 	}
1038dc13fee6SSepherosa Ziehau }
1039dc13fee6SSepherosa Ziehau 
104015516c77SSepherosa Ziehau static int
104115516c77SSepherosa Ziehau hn_get_txswq_depth(const struct hn_tx_ring *txr)
104215516c77SSepherosa Ziehau {
104315516c77SSepherosa Ziehau 
104415516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_cnt > 0, ("tx ring is not setup yet"));
104515516c77SSepherosa Ziehau 	if (hn_tx_swq_depth < txr->hn_txdesc_cnt)
104615516c77SSepherosa Ziehau 		return txr->hn_txdesc_cnt;
104715516c77SSepherosa Ziehau 	return hn_tx_swq_depth;
104815516c77SSepherosa Ziehau }
104915516c77SSepherosa Ziehau 
105015516c77SSepherosa Ziehau static int
105115516c77SSepherosa Ziehau hn_rss_reconfig(struct hn_softc *sc)
105215516c77SSepherosa Ziehau {
105315516c77SSepherosa Ziehau 	int error;
105415516c77SSepherosa Ziehau 
105515516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
105615516c77SSepherosa Ziehau 
105715516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
105815516c77SSepherosa Ziehau 		return (ENXIO);
105915516c77SSepherosa Ziehau 
106015516c77SSepherosa Ziehau 	/*
106115516c77SSepherosa Ziehau 	 * Disable RSS first.
106215516c77SSepherosa Ziehau 	 *
106315516c77SSepherosa Ziehau 	 * NOTE:
106415516c77SSepherosa Ziehau 	 * Direct reconfiguration by setting the UNCHG flags does
106515516c77SSepherosa Ziehau 	 * _not_ work properly.
106615516c77SSepherosa Ziehau 	 */
106715516c77SSepherosa Ziehau 	if (bootverbose)
106815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "disable RSS\n");
106915516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_DISABLE);
107015516c77SSepherosa Ziehau 	if (error) {
107115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS disable failed\n");
107215516c77SSepherosa Ziehau 		return (error);
107315516c77SSepherosa Ziehau 	}
107415516c77SSepherosa Ziehau 
107515516c77SSepherosa Ziehau 	/*
107615516c77SSepherosa Ziehau 	 * Reenable the RSS w/ the updated RSS key or indirect
107715516c77SSepherosa Ziehau 	 * table.
107815516c77SSepherosa Ziehau 	 */
107915516c77SSepherosa Ziehau 	if (bootverbose)
108015516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "reconfig RSS\n");
108115516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
108215516c77SSepherosa Ziehau 	if (error) {
108315516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS reconfig failed\n");
108415516c77SSepherosa Ziehau 		return (error);
108515516c77SSepherosa Ziehau 	}
108615516c77SSepherosa Ziehau 	return (0);
108715516c77SSepherosa Ziehau }
108815516c77SSepherosa Ziehau 
108915516c77SSepherosa Ziehau static void
1090afd4971bSSepherosa Ziehau hn_rss_ind_fixup(struct hn_softc *sc)
109115516c77SSepherosa Ziehau {
109215516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
1093afd4971bSSepherosa Ziehau 	int i, nchan;
109415516c77SSepherosa Ziehau 
1095afd4971bSSepherosa Ziehau 	nchan = sc->hn_rx_ring_inuse;
109615516c77SSepherosa Ziehau 	KASSERT(nchan > 1, ("invalid # of channels %d", nchan));
109715516c77SSepherosa Ziehau 
109815516c77SSepherosa Ziehau 	/*
109915516c77SSepherosa Ziehau 	 * Check indirect table to make sure that all channels in it
110015516c77SSepherosa Ziehau 	 * can be used.
110115516c77SSepherosa Ziehau 	 */
110215516c77SSepherosa Ziehau 	for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
110315516c77SSepherosa Ziehau 		if (rss->rss_ind[i] >= nchan) {
110415516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp,
110515516c77SSepherosa Ziehau 			    "RSS indirect table %d fixup: %u -> %d\n",
110615516c77SSepherosa Ziehau 			    i, rss->rss_ind[i], nchan - 1);
110715516c77SSepherosa Ziehau 			rss->rss_ind[i] = nchan - 1;
110815516c77SSepherosa Ziehau 		}
110915516c77SSepherosa Ziehau 	}
111015516c77SSepherosa Ziehau }
111115516c77SSepherosa Ziehau 
111215516c77SSepherosa Ziehau static int
111315516c77SSepherosa Ziehau hn_ifmedia_upd(struct ifnet *ifp __unused)
111415516c77SSepherosa Ziehau {
111515516c77SSepherosa Ziehau 
111615516c77SSepherosa Ziehau 	return EOPNOTSUPP;
111715516c77SSepherosa Ziehau }
111815516c77SSepherosa Ziehau 
111915516c77SSepherosa Ziehau static void
112015516c77SSepherosa Ziehau hn_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
112115516c77SSepherosa Ziehau {
112215516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
112315516c77SSepherosa Ziehau 
112415516c77SSepherosa Ziehau 	ifmr->ifm_status = IFM_AVALID;
112515516c77SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
112615516c77SSepherosa Ziehau 
112715516c77SSepherosa Ziehau 	if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) {
112815516c77SSepherosa Ziehau 		ifmr->ifm_active |= IFM_NONE;
112915516c77SSepherosa Ziehau 		return;
113015516c77SSepherosa Ziehau 	}
113115516c77SSepherosa Ziehau 	ifmr->ifm_status |= IFM_ACTIVE;
113215516c77SSepherosa Ziehau 	ifmr->ifm_active |= IFM_10G_T | IFM_FDX;
113315516c77SSepherosa Ziehau }
113415516c77SSepherosa Ziehau 
11355bdfd3fdSDexuan Cui static void
1136962f0357SSepherosa Ziehau hn_rxvf_set_task(void *xarg, int pending __unused)
11375bdfd3fdSDexuan Cui {
1138962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg *arg = xarg;
11395bdfd3fdSDexuan Cui 
1140962f0357SSepherosa Ziehau 	arg->rxr->hn_rxvf_ifp = arg->vf_ifp;
11415bdfd3fdSDexuan Cui }
11425bdfd3fdSDexuan Cui 
11435bdfd3fdSDexuan Cui static void
1144962f0357SSepherosa Ziehau hn_rxvf_set(struct hn_softc *sc, struct ifnet *vf_ifp)
11455bdfd3fdSDexuan Cui {
11465bdfd3fdSDexuan Cui 	struct hn_rx_ring *rxr;
1147962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg arg;
11485bdfd3fdSDexuan Cui 	struct task task;
11495bdfd3fdSDexuan Cui 	int i;
11505bdfd3fdSDexuan Cui 
11515bdfd3fdSDexuan Cui 	HN_LOCK_ASSERT(sc);
11525bdfd3fdSDexuan Cui 
1153962f0357SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_rxvf_set_task, &arg);
11545bdfd3fdSDexuan Cui 
11555bdfd3fdSDexuan Cui 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
11565bdfd3fdSDexuan Cui 		rxr = &sc->hn_rx_ring[i];
11575bdfd3fdSDexuan Cui 
11585bdfd3fdSDexuan Cui 		if (i < sc->hn_rx_ring_inuse) {
1159962f0357SSepherosa Ziehau 			arg.rxr = rxr;
1160962f0357SSepherosa Ziehau 			arg.vf_ifp = vf_ifp;
11615bdfd3fdSDexuan Cui 			vmbus_chan_run_task(rxr->hn_chan, &task);
11625bdfd3fdSDexuan Cui 		} else {
1163962f0357SSepherosa Ziehau 			rxr->hn_rxvf_ifp = vf_ifp;
11645bdfd3fdSDexuan Cui 		}
11655bdfd3fdSDexuan Cui 	}
11665bdfd3fdSDexuan Cui }
11675bdfd3fdSDexuan Cui 
1168962f0357SSepherosa Ziehau static bool
1169499c3e17SSepherosa Ziehau hn_ismyvf(const struct hn_softc *sc, const struct ifnet *ifp)
1170499c3e17SSepherosa Ziehau {
1171499c3e17SSepherosa Ziehau 	const struct ifnet *hn_ifp;
1172499c3e17SSepherosa Ziehau 
1173499c3e17SSepherosa Ziehau 	hn_ifp = sc->hn_ifp;
1174499c3e17SSepherosa Ziehau 
1175499c3e17SSepherosa Ziehau 	if (ifp == hn_ifp)
1176499c3e17SSepherosa Ziehau 		return (false);
1177499c3e17SSepherosa Ziehau 
1178499c3e17SSepherosa Ziehau 	if (ifp->if_alloctype != IFT_ETHER)
1179499c3e17SSepherosa Ziehau 		return (false);
1180499c3e17SSepherosa Ziehau 
1181499c3e17SSepherosa Ziehau 	/* Ignore lagg/vlan interfaces */
1182499c3e17SSepherosa Ziehau 	if (strcmp(ifp->if_dname, "lagg") == 0 ||
1183499c3e17SSepherosa Ziehau 	    strcmp(ifp->if_dname, "vlan") == 0)
1184499c3e17SSepherosa Ziehau 		return (false);
1185499c3e17SSepherosa Ziehau 
1186d76fb49fSDexuan Cui 	/*
1187d76fb49fSDexuan Cui 	 * During detach events ifp->if_addr might be NULL.
1188d76fb49fSDexuan Cui 	 * Make sure the bcmp() below doesn't panic on that:
1189d76fb49fSDexuan Cui 	 */
1190d76fb49fSDexuan Cui 	if (ifp->if_addr == NULL || hn_ifp->if_addr == NULL)
1191d76fb49fSDexuan Cui 		return (false);
1192d76fb49fSDexuan Cui 
1193499c3e17SSepherosa Ziehau 	if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0)
1194499c3e17SSepherosa Ziehau 		return (false);
1195499c3e17SSepherosa Ziehau 
1196499c3e17SSepherosa Ziehau 	return (true);
1197499c3e17SSepherosa Ziehau }
1198499c3e17SSepherosa Ziehau 
11995bdfd3fdSDexuan Cui static void
1200962f0357SSepherosa Ziehau hn_rxvf_change(struct hn_softc *sc, struct ifnet *ifp, bool rxvf)
12015bdfd3fdSDexuan Cui {
12025bdfd3fdSDexuan Cui 	struct ifnet *hn_ifp;
12035bdfd3fdSDexuan Cui 
12045bdfd3fdSDexuan Cui 	HN_LOCK(sc);
12055bdfd3fdSDexuan Cui 
12065bdfd3fdSDexuan Cui 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
12075bdfd3fdSDexuan Cui 		goto out;
12085bdfd3fdSDexuan Cui 
1209499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1210499c3e17SSepherosa Ziehau 		goto out;
12115bdfd3fdSDexuan Cui 	hn_ifp = sc->hn_ifp;
12125bdfd3fdSDexuan Cui 
1213962f0357SSepherosa Ziehau 	if (rxvf) {
1214962f0357SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_RXVF)
12155bdfd3fdSDexuan Cui 			goto out;
12165bdfd3fdSDexuan Cui 
1217962f0357SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_RXVF;
12185bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
12195bdfd3fdSDexuan Cui 	} else {
1220962f0357SSepherosa Ziehau 		if (!(sc->hn_flags & HN_FLAG_RXVF))
12215bdfd3fdSDexuan Cui 			goto out;
12225bdfd3fdSDexuan Cui 
1223962f0357SSepherosa Ziehau 		sc->hn_flags &= ~HN_FLAG_RXVF;
1224499c3e17SSepherosa Ziehau 		if (hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
12255bdfd3fdSDexuan Cui 			hn_rxfilter_config(sc);
12265bdfd3fdSDexuan Cui 		else
12275bdfd3fdSDexuan Cui 			hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE);
12285bdfd3fdSDexuan Cui 	}
12295bdfd3fdSDexuan Cui 
12305bdfd3fdSDexuan Cui 	hn_nvs_set_datapath(sc,
12319c6cae24SSepherosa Ziehau 	    rxvf ? HN_NVS_DATAPATH_VF : HN_NVS_DATAPATH_SYNTH);
12325bdfd3fdSDexuan Cui 
1233962f0357SSepherosa Ziehau 	hn_rxvf_set(sc, rxvf ? ifp : NULL);
12345bdfd3fdSDexuan Cui 
1235962f0357SSepherosa Ziehau 	if (rxvf) {
1236642ec226SSepherosa Ziehau 		hn_vf_rss_fixup(sc, true);
12375bdfd3fdSDexuan Cui 		hn_suspend_mgmt(sc);
12385bdfd3fdSDexuan Cui 		sc->hn_link_flags &=
12395bdfd3fdSDexuan Cui 		    ~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG);
1240499c3e17SSepherosa Ziehau 		if_link_state_change(hn_ifp, LINK_STATE_DOWN);
12415bdfd3fdSDexuan Cui 	} else {
1242642ec226SSepherosa Ziehau 		hn_vf_rss_restore(sc);
12435bdfd3fdSDexuan Cui 		hn_resume_mgmt(sc);
12445bdfd3fdSDexuan Cui 	}
12455bdfd3fdSDexuan Cui 
1246962f0357SSepherosa Ziehau 	devctl_notify("HYPERV_NIC_VF", hn_ifp->if_xname,
1247962f0357SSepherosa Ziehau 	    rxvf ? "VF_UP" : "VF_DOWN", NULL);
124833408a34SDexuan Cui 
1249962f0357SSepherosa Ziehau 	if (bootverbose) {
1250962f0357SSepherosa Ziehau 		if_printf(hn_ifp, "datapath is switched %s %s\n",
1251962f0357SSepherosa Ziehau 		    rxvf ? "to" : "from", ifp->if_xname);
1252962f0357SSepherosa Ziehau 	}
12535bdfd3fdSDexuan Cui out:
12545bdfd3fdSDexuan Cui 	HN_UNLOCK(sc);
12555bdfd3fdSDexuan Cui }
12565bdfd3fdSDexuan Cui 
12575bdfd3fdSDexuan Cui static void
12585bdfd3fdSDexuan Cui hn_ifnet_event(void *arg, struct ifnet *ifp, int event)
12595bdfd3fdSDexuan Cui {
1260962f0357SSepherosa Ziehau 
12615bdfd3fdSDexuan Cui 	if (event != IFNET_EVENT_UP && event != IFNET_EVENT_DOWN)
12625bdfd3fdSDexuan Cui 		return;
1263962f0357SSepherosa Ziehau 	hn_rxvf_change(arg, ifp, event == IFNET_EVENT_UP);
12645bdfd3fdSDexuan Cui }
12655bdfd3fdSDexuan Cui 
12665bdfd3fdSDexuan Cui static void
12675bdfd3fdSDexuan Cui hn_ifaddr_event(void *arg, struct ifnet *ifp)
12685bdfd3fdSDexuan Cui {
1269962f0357SSepherosa Ziehau 
1270962f0357SSepherosa Ziehau 	hn_rxvf_change(arg, ifp, ifp->if_flags & IFF_UP);
12715bdfd3fdSDexuan Cui }
12725bdfd3fdSDexuan Cui 
12739c6cae24SSepherosa Ziehau static int
12749c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetcaps(struct hn_softc *sc, struct ifreq *ifr)
12759c6cae24SSepherosa Ziehau {
12769c6cae24SSepherosa Ziehau 	struct ifnet *ifp, *vf_ifp;
12779c6cae24SSepherosa Ziehau 	uint64_t tmp;
12789c6cae24SSepherosa Ziehau 	int error;
12799c6cae24SSepherosa Ziehau 
12809c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
12819c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
12829c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
12839c6cae24SSepherosa Ziehau 
12849c6cae24SSepherosa Ziehau 	/*
12859c6cae24SSepherosa Ziehau 	 * Fix up requested capabilities w/ supported capabilities,
12869c6cae24SSepherosa Ziehau 	 * since the supported capabilities could have been changed.
12879c6cae24SSepherosa Ziehau 	 */
12889c6cae24SSepherosa Ziehau 	ifr->ifr_reqcap &= ifp->if_capabilities;
12899c6cae24SSepherosa Ziehau 	/* Pass SIOCSIFCAP to VF. */
12909c6cae24SSepherosa Ziehau 	error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFCAP, (caddr_t)ifr);
12919c6cae24SSepherosa Ziehau 
12929c6cae24SSepherosa Ziehau 	/*
12939c6cae24SSepherosa Ziehau 	 * NOTE:
12949c6cae24SSepherosa Ziehau 	 * The error will be propagated to the callers, however, it
12959c6cae24SSepherosa Ziehau 	 * is _not_ useful here.
12969c6cae24SSepherosa Ziehau 	 */
12979c6cae24SSepherosa Ziehau 
12989c6cae24SSepherosa Ziehau 	/*
12999c6cae24SSepherosa Ziehau 	 * Merge VF's enabled capabilities.
13009c6cae24SSepherosa Ziehau 	 */
13019c6cae24SSepherosa Ziehau 	ifp->if_capenable = vf_ifp->if_capenable & ifp->if_capabilities;
13029c6cae24SSepherosa Ziehau 
13039c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & HN_CSUM_IP_HWASSIST(sc);
13049c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TXCSUM)
13059c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
13069c6cae24SSepherosa Ziehau 	else
13079c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
13089c6cae24SSepherosa Ziehau 
13099c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & HN_CSUM_IP6_HWASSIST(sc);
13109c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
13119c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
13129c6cae24SSepherosa Ziehau 	else
13139c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
13149c6cae24SSepherosa Ziehau 
13159c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & CSUM_IP_TSO;
13169c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TSO4)
13179c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
13189c6cae24SSepherosa Ziehau 	else
13199c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
13209c6cae24SSepherosa Ziehau 
13219c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & CSUM_IP6_TSO;
13229c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TSO6)
13239c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
13249c6cae24SSepherosa Ziehau 	else
13259c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
13269c6cae24SSepherosa Ziehau 
13279c6cae24SSepherosa Ziehau 	return (error);
13289c6cae24SSepherosa Ziehau }
13299c6cae24SSepherosa Ziehau 
13309c6cae24SSepherosa Ziehau static int
13319c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetflags(struct hn_softc *sc)
13329c6cae24SSepherosa Ziehau {
13339c6cae24SSepherosa Ziehau 	struct ifnet *vf_ifp;
13349c6cae24SSepherosa Ziehau 	struct ifreq ifr;
13359c6cae24SSepherosa Ziehau 
13369c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13379c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
13389c6cae24SSepherosa Ziehau 
13399c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
13409c6cae24SSepherosa Ziehau 	strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
13419c6cae24SSepherosa Ziehau 	ifr.ifr_flags = vf_ifp->if_flags & 0xffff;
13429c6cae24SSepherosa Ziehau 	ifr.ifr_flagshigh = vf_ifp->if_flags >> 16;
13439c6cae24SSepherosa Ziehau 	return (vf_ifp->if_ioctl(vf_ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
13449c6cae24SSepherosa Ziehau }
13459c6cae24SSepherosa Ziehau 
13469c6cae24SSepherosa Ziehau static void
13479c6cae24SSepherosa Ziehau hn_xpnt_vf_saveifflags(struct hn_softc *sc)
13489c6cae24SSepherosa Ziehau {
13499c6cae24SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
13509c6cae24SSepherosa Ziehau 	int allmulti = 0;
13519c6cae24SSepherosa Ziehau 
13529c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13539c6cae24SSepherosa Ziehau 
13549c6cae24SSepherosa Ziehau 	/* XXX vlan(4) style mcast addr maintenance */
1355d7c5a620SMatt Macy 	if (!CK_STAILQ_EMPTY(&ifp->if_multiaddrs))
13569c6cae24SSepherosa Ziehau 		allmulti = IFF_ALLMULTI;
13579c6cae24SSepherosa Ziehau 
13589c6cae24SSepherosa Ziehau 	/* Always set the VF's if_flags */
13599c6cae24SSepherosa Ziehau 	sc->hn_vf_ifp->if_flags = ifp->if_flags | allmulti;
13609c6cae24SSepherosa Ziehau }
13619c6cae24SSepherosa Ziehau 
13629c6cae24SSepherosa Ziehau static void
13639c6cae24SSepherosa Ziehau hn_xpnt_vf_input(struct ifnet *vf_ifp, struct mbuf *m)
13649c6cae24SSepherosa Ziehau {
13659c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
13669c6cae24SSepherosa Ziehau 	struct ifnet *hn_ifp = NULL;
13679c6cae24SSepherosa Ziehau 	struct mbuf *mn;
13689c6cae24SSepherosa Ziehau 
13699c6cae24SSepherosa Ziehau 	/*
13709c6cae24SSepherosa Ziehau 	 * XXX racy, if hn(4) ever detached.
13719c6cae24SSepherosa Ziehau 	 */
13729c6cae24SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
13739c6cae24SSepherosa Ziehau 	if (vf_ifp->if_index < hn_vfmap_size)
13749c6cae24SSepherosa Ziehau 		hn_ifp = hn_vfmap[vf_ifp->if_index];
13759c6cae24SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
13769c6cae24SSepherosa Ziehau 
13779c6cae24SSepherosa Ziehau 	if (hn_ifp != NULL) {
13789c6cae24SSepherosa Ziehau 		for (mn = m; mn != NULL; mn = mn->m_nextpkt) {
13793bed4e54SSepherosa Ziehau 			/*
13803bed4e54SSepherosa Ziehau 			 * Allow tapping on the VF.
13813bed4e54SSepherosa Ziehau 			 */
13829c6cae24SSepherosa Ziehau 			ETHER_BPF_MTAP(vf_ifp, mn);
13833bed4e54SSepherosa Ziehau 
13843bed4e54SSepherosa Ziehau 			/*
13853bed4e54SSepherosa Ziehau 			 * Update VF stats.
13863bed4e54SSepherosa Ziehau 			 */
13873bed4e54SSepherosa Ziehau 			if ((vf_ifp->if_capenable & IFCAP_HWSTATS) == 0) {
13883bed4e54SSepherosa Ziehau 				if_inc_counter(vf_ifp, IFCOUNTER_IBYTES,
13893bed4e54SSepherosa Ziehau 				    mn->m_pkthdr.len);
13903bed4e54SSepherosa Ziehau 			}
13913bed4e54SSepherosa Ziehau 			/*
13923bed4e54SSepherosa Ziehau 			 * XXX IFCOUNTER_IMCAST
13933bed4e54SSepherosa Ziehau 			 * This stat updating is kinda invasive, since it
13943bed4e54SSepherosa Ziehau 			 * requires two checks on the mbuf: the length check
13953bed4e54SSepherosa Ziehau 			 * and the ethernet header check.  As of this write,
13963bed4e54SSepherosa Ziehau 			 * all multicast packets go directly to hn(4), which
13973bed4e54SSepherosa Ziehau 			 * makes imcast stat updating in the VF a try in vian.
13983bed4e54SSepherosa Ziehau 			 */
13993bed4e54SSepherosa Ziehau 
14003bed4e54SSepherosa Ziehau 			/*
14013bed4e54SSepherosa Ziehau 			 * Fix up rcvif and increase hn(4)'s ipackets.
14023bed4e54SSepherosa Ziehau 			 */
14039c6cae24SSepherosa Ziehau 			mn->m_pkthdr.rcvif = hn_ifp;
14049c6cae24SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
14059c6cae24SSepherosa Ziehau 		}
14063bed4e54SSepherosa Ziehau 		/*
14073bed4e54SSepherosa Ziehau 		 * Go through hn(4)'s if_input.
14083bed4e54SSepherosa Ziehau 		 */
14099c6cae24SSepherosa Ziehau 		hn_ifp->if_input(hn_ifp, m);
14109c6cae24SSepherosa Ziehau 	} else {
14119c6cae24SSepherosa Ziehau 		/*
14129c6cae24SSepherosa Ziehau 		 * In the middle of the transition; free this
14139c6cae24SSepherosa Ziehau 		 * mbuf chain.
14149c6cae24SSepherosa Ziehau 		 */
14159c6cae24SSepherosa Ziehau 		while (m != NULL) {
14169c6cae24SSepherosa Ziehau 			mn = m->m_nextpkt;
14179c6cae24SSepherosa Ziehau 			m->m_nextpkt = NULL;
14189c6cae24SSepherosa Ziehau 			m_freem(m);
14199c6cae24SSepherosa Ziehau 			m = mn;
14209c6cae24SSepherosa Ziehau 		}
14219c6cae24SSepherosa Ziehau 	}
14229c6cae24SSepherosa Ziehau }
14239c6cae24SSepherosa Ziehau 
14249c6cae24SSepherosa Ziehau static void
14259c6cae24SSepherosa Ziehau hn_mtu_change_fixup(struct hn_softc *sc)
14269c6cae24SSepherosa Ziehau {
14279c6cae24SSepherosa Ziehau 	struct ifnet *ifp;
14289c6cae24SSepherosa Ziehau 
14299c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
14309c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
14319c6cae24SSepherosa Ziehau 
14329c6cae24SSepherosa Ziehau 	hn_set_tso_maxsize(sc, hn_tso_maxlen, ifp->if_mtu);
14339c6cae24SSepherosa Ziehau #if __FreeBSD_version >= 1100099
14349c6cae24SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_lro.lro_length_lim < HN_LRO_LENLIM_MIN(ifp))
14359c6cae24SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp));
14369c6cae24SSepherosa Ziehau #endif
14379c6cae24SSepherosa Ziehau }
14389c6cae24SSepherosa Ziehau 
1439642ec226SSepherosa Ziehau static uint32_t
1440642ec226SSepherosa Ziehau hn_rss_type_fromndis(uint32_t rss_hash)
1441642ec226SSepherosa Ziehau {
1442642ec226SSepherosa Ziehau 	uint32_t types = 0;
1443642ec226SSepherosa Ziehau 
1444642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV4)
1445642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV4;
1446642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV4)
1447642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV4;
1448642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV6)
1449642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV6;
1450642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_IPV6_EX)
1451642ec226SSepherosa Ziehau 		types |= RSS_TYPE_IPV6_EX;
1452642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV6)
1453642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV6;
1454642ec226SSepherosa Ziehau 	if (rss_hash & NDIS_HASH_TCP_IPV6_EX)
1455642ec226SSepherosa Ziehau 		types |= RSS_TYPE_TCP_IPV6_EX;
14566f12c42eSSepherosa Ziehau 	if (rss_hash & NDIS_HASH_UDP_IPV4_X)
14576f12c42eSSepherosa Ziehau 		types |= RSS_TYPE_UDP_IPV4;
1458642ec226SSepherosa Ziehau 	return (types);
1459642ec226SSepherosa Ziehau }
1460642ec226SSepherosa Ziehau 
1461642ec226SSepherosa Ziehau static uint32_t
1462642ec226SSepherosa Ziehau hn_rss_type_tondis(uint32_t types)
1463642ec226SSepherosa Ziehau {
1464642ec226SSepherosa Ziehau 	uint32_t rss_hash = 0;
1465642ec226SSepherosa Ziehau 
14666f12c42eSSepherosa Ziehau 	KASSERT((types & (RSS_TYPE_UDP_IPV6 | RSS_TYPE_UDP_IPV6_EX)) == 0,
14676f12c42eSSepherosa Ziehau 	    ("UDP6 and UDP6EX are not supported"));
1468642ec226SSepherosa Ziehau 
1469642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV4)
1470642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV4;
1471642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV4)
1472642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV4;
1473642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV6)
1474642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV6;
1475642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_IPV6_EX)
1476642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_IPV6_EX;
1477642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV6)
1478642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV6;
1479642ec226SSepherosa Ziehau 	if (types & RSS_TYPE_TCP_IPV6_EX)
1480642ec226SSepherosa Ziehau 		rss_hash |= NDIS_HASH_TCP_IPV6_EX;
14816f12c42eSSepherosa Ziehau 	if (types & RSS_TYPE_UDP_IPV4)
14826f12c42eSSepherosa Ziehau 		rss_hash |= NDIS_HASH_UDP_IPV4_X;
1483642ec226SSepherosa Ziehau 	return (rss_hash);
1484642ec226SSepherosa Ziehau }
1485642ec226SSepherosa Ziehau 
1486642ec226SSepherosa Ziehau static void
1487642ec226SSepherosa Ziehau hn_rss_mbuf_hash(struct hn_softc *sc, uint32_t mbuf_hash)
1488642ec226SSepherosa Ziehau {
1489642ec226SSepherosa Ziehau 	int i;
1490642ec226SSepherosa Ziehau 
1491642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1492642ec226SSepherosa Ziehau 
1493642ec226SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1494642ec226SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_mbuf_hash = mbuf_hash;
1495642ec226SSepherosa Ziehau }
1496642ec226SSepherosa Ziehau 
1497642ec226SSepherosa Ziehau static void
1498642ec226SSepherosa Ziehau hn_vf_rss_fixup(struct hn_softc *sc, bool reconf)
1499642ec226SSepherosa Ziehau {
1500642ec226SSepherosa Ziehau 	struct ifnet *ifp, *vf_ifp;
1501642ec226SSepherosa Ziehau 	struct ifrsshash ifrh;
1502642ec226SSepherosa Ziehau 	struct ifrsskey ifrk;
1503642ec226SSepherosa Ziehau 	int error;
1504642ec226SSepherosa Ziehau 	uint32_t my_types, diff_types, mbuf_types = 0;
1505642ec226SSepherosa Ziehau 
1506642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1507642ec226SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
1508642ec226SSepherosa Ziehau 	    ("%s: synthetic parts are not attached", sc->hn_ifp->if_xname));
1509642ec226SSepherosa Ziehau 
1510642ec226SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
1511642ec226SSepherosa Ziehau 		/* No RSS on synthetic parts; done. */
1512642ec226SSepherosa Ziehau 		return;
1513642ec226SSepherosa Ziehau 	}
1514642ec226SSepherosa Ziehau 	if ((sc->hn_rss_hcap & NDIS_HASH_FUNCTION_TOEPLITZ) == 0) {
1515642ec226SSepherosa Ziehau 		/* Synthetic parts do not support Toeplitz; done. */
1516642ec226SSepherosa Ziehau 		return;
1517642ec226SSepherosa Ziehau 	}
1518642ec226SSepherosa Ziehau 
1519642ec226SSepherosa Ziehau 	ifp = sc->hn_ifp;
1520642ec226SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
1521642ec226SSepherosa Ziehau 
1522642ec226SSepherosa Ziehau 	/*
1523642ec226SSepherosa Ziehau 	 * Extract VF's RSS key.  Only 40 bytes key for Toeplitz is
1524642ec226SSepherosa Ziehau 	 * supported.
1525642ec226SSepherosa Ziehau 	 */
1526642ec226SSepherosa Ziehau 	memset(&ifrk, 0, sizeof(ifrk));
1527642ec226SSepherosa Ziehau 	strlcpy(ifrk.ifrk_name, vf_ifp->if_xname, sizeof(ifrk.ifrk_name));
1528642ec226SSepherosa Ziehau 	error = vf_ifp->if_ioctl(vf_ifp, SIOCGIFRSSKEY, (caddr_t)&ifrk);
1529642ec226SSepherosa Ziehau 	if (error) {
1530a3b413afSHans Petter Selasky 		if_printf(ifp, "%s SIOCGIFRSSKEY failed: %d\n",
1531642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, error);
1532642ec226SSepherosa Ziehau 		goto done;
1533642ec226SSepherosa Ziehau 	}
1534642ec226SSepherosa Ziehau 	if (ifrk.ifrk_func != RSS_FUNC_TOEPLITZ) {
1535642ec226SSepherosa Ziehau 		if_printf(ifp, "%s RSS function %u is not Toeplitz\n",
1536642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, ifrk.ifrk_func);
1537642ec226SSepherosa Ziehau 		goto done;
1538642ec226SSepherosa Ziehau 	}
1539642ec226SSepherosa Ziehau 	if (ifrk.ifrk_keylen != NDIS_HASH_KEYSIZE_TOEPLITZ) {
1540642ec226SSepherosa Ziehau 		if_printf(ifp, "%s invalid RSS Toeplitz key length %d\n",
1541642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, ifrk.ifrk_keylen);
1542642ec226SSepherosa Ziehau 		goto done;
1543642ec226SSepherosa Ziehau 	}
1544642ec226SSepherosa Ziehau 
1545642ec226SSepherosa Ziehau 	/*
1546642ec226SSepherosa Ziehau 	 * Extract VF's RSS hash.  Only Toeplitz is supported.
1547642ec226SSepherosa Ziehau 	 */
1548642ec226SSepherosa Ziehau 	memset(&ifrh, 0, sizeof(ifrh));
1549642ec226SSepherosa Ziehau 	strlcpy(ifrh.ifrh_name, vf_ifp->if_xname, sizeof(ifrh.ifrh_name));
1550642ec226SSepherosa Ziehau 	error = vf_ifp->if_ioctl(vf_ifp, SIOCGIFRSSHASH, (caddr_t)&ifrh);
1551642ec226SSepherosa Ziehau 	if (error) {
1552642ec226SSepherosa Ziehau 		if_printf(ifp, "%s SIOCGRSSHASH failed: %d\n",
1553642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, error);
1554642ec226SSepherosa Ziehau 		goto done;
1555642ec226SSepherosa Ziehau 	}
1556642ec226SSepherosa Ziehau 	if (ifrh.ifrh_func != RSS_FUNC_TOEPLITZ) {
1557642ec226SSepherosa Ziehau 		if_printf(ifp, "%s RSS function %u is not Toeplitz\n",
1558642ec226SSepherosa Ziehau 		    vf_ifp->if_xname, ifrh.ifrh_func);
1559642ec226SSepherosa Ziehau 		goto done;
1560642ec226SSepherosa Ziehau 	}
1561642ec226SSepherosa Ziehau 
1562642ec226SSepherosa Ziehau 	my_types = hn_rss_type_fromndis(sc->hn_rss_hcap);
1563642ec226SSepherosa Ziehau 	if ((ifrh.ifrh_types & my_types) == 0) {
1564642ec226SSepherosa Ziehau 		/* This disables RSS; ignore it then */
1565642ec226SSepherosa Ziehau 		if_printf(ifp, "%s intersection of RSS types failed.  "
1566642ec226SSepherosa Ziehau 		    "VF %#x, mine %#x\n", vf_ifp->if_xname,
1567642ec226SSepherosa Ziehau 		    ifrh.ifrh_types, my_types);
1568642ec226SSepherosa Ziehau 		goto done;
1569642ec226SSepherosa Ziehau 	}
1570642ec226SSepherosa Ziehau 
1571642ec226SSepherosa Ziehau 	diff_types = my_types ^ ifrh.ifrh_types;
1572642ec226SSepherosa Ziehau 	my_types &= ifrh.ifrh_types;
1573642ec226SSepherosa Ziehau 	mbuf_types = my_types;
1574642ec226SSepherosa Ziehau 
1575642ec226SSepherosa Ziehau 	/*
1576642ec226SSepherosa Ziehau 	 * Detect RSS hash value/type confliction.
1577642ec226SSepherosa Ziehau 	 *
1578642ec226SSepherosa Ziehau 	 * NOTE:
1579642ec226SSepherosa Ziehau 	 * We don't disable the hash type, but stop delivery the hash
1580642ec226SSepherosa Ziehau 	 * value/type through mbufs on RX path.
15816f12c42eSSepherosa Ziehau 	 *
15826f12c42eSSepherosa Ziehau 	 * XXX If HN_CAP_UDPHASH is set in hn_caps, then UDP 4-tuple
15836f12c42eSSepherosa Ziehau 	 * hash is delivered with type of TCP_IPV4.  This means if
15846f12c42eSSepherosa Ziehau 	 * UDP_IPV4 is enabled, then TCP_IPV4 should be forced, at
15856f12c42eSSepherosa Ziehau 	 * least to hn_mbuf_hash.  However, given that _all_ of the
15866f12c42eSSepherosa Ziehau 	 * NICs implement TCP_IPV4, this will _not_ impose any issues
15876f12c42eSSepherosa Ziehau 	 * here.
1588642ec226SSepherosa Ziehau 	 */
1589642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV4) &&
1590642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1591642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV4 | RSS_TYPE_UDP_IPV4))) {
1592642ec226SSepherosa Ziehau 		/* Conflict; disable IPV4 hash type/value delivery. */
1593642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV4 mbuf hash delivery\n");
1594642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV4;
1595642ec226SSepherosa Ziehau 	}
1596642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV6) &&
1597642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1598642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV6 | RSS_TYPE_UDP_IPV6 |
1599642ec226SSepherosa Ziehau 	      RSS_TYPE_TCP_IPV6_EX | RSS_TYPE_UDP_IPV6_EX |
1600642ec226SSepherosa Ziehau 	      RSS_TYPE_IPV6_EX))) {
1601642ec226SSepherosa Ziehau 		/* Conflict; disable IPV6 hash type/value delivery. */
1602642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV6 mbuf hash delivery\n");
1603642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV6;
1604642ec226SSepherosa Ziehau 	}
1605642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_IPV6_EX) &&
1606642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types &
1607642ec226SSepherosa Ziehau 	     (RSS_TYPE_TCP_IPV6 | RSS_TYPE_UDP_IPV6 |
1608642ec226SSepherosa Ziehau 	      RSS_TYPE_TCP_IPV6_EX | RSS_TYPE_UDP_IPV6_EX |
1609642ec226SSepherosa Ziehau 	      RSS_TYPE_IPV6))) {
1610642ec226SSepherosa Ziehau 		/* Conflict; disable IPV6_EX hash type/value delivery. */
1611642ec226SSepherosa Ziehau 		if_printf(ifp, "disable IPV6_EX mbuf hash delivery\n");
1612642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_IPV6_EX;
1613642ec226SSepherosa Ziehau 	}
1614642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_TCP_IPV6) &&
1615642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_TCP_IPV6_EX)) {
1616642ec226SSepherosa Ziehau 		/* Conflict; disable TCP_IPV6 hash type/value delivery. */
1617642ec226SSepherosa Ziehau 		if_printf(ifp, "disable TCP_IPV6 mbuf hash delivery\n");
1618642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_TCP_IPV6;
1619642ec226SSepherosa Ziehau 	}
1620642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_TCP_IPV6_EX) &&
1621642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_TCP_IPV6)) {
1622642ec226SSepherosa Ziehau 		/* Conflict; disable TCP_IPV6_EX hash type/value delivery. */
1623642ec226SSepherosa Ziehau 		if_printf(ifp, "disable TCP_IPV6_EX mbuf hash delivery\n");
1624642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_TCP_IPV6_EX;
1625642ec226SSepherosa Ziehau 	}
1626642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_UDP_IPV6) &&
1627642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_UDP_IPV6_EX)) {
1628642ec226SSepherosa Ziehau 		/* Conflict; disable UDP_IPV6 hash type/value delivery. */
1629642ec226SSepherosa Ziehau 		if_printf(ifp, "disable UDP_IPV6 mbuf hash delivery\n");
1630642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_UDP_IPV6;
1631642ec226SSepherosa Ziehau 	}
1632642ec226SSepherosa Ziehau 	if ((my_types & RSS_TYPE_UDP_IPV6_EX) &&
1633642ec226SSepherosa Ziehau 	    (diff_types & ifrh.ifrh_types & RSS_TYPE_UDP_IPV6)) {
1634642ec226SSepherosa Ziehau 		/* Conflict; disable UDP_IPV6_EX hash type/value delivery. */
1635642ec226SSepherosa Ziehau 		if_printf(ifp, "disable UDP_IPV6_EX mbuf hash delivery\n");
1636642ec226SSepherosa Ziehau 		mbuf_types &= ~RSS_TYPE_UDP_IPV6_EX;
1637642ec226SSepherosa Ziehau 	}
1638642ec226SSepherosa Ziehau 
1639642ec226SSepherosa Ziehau 	/*
1640642ec226SSepherosa Ziehau 	 * Indirect table does not matter.
1641642ec226SSepherosa Ziehau 	 */
1642642ec226SSepherosa Ziehau 
1643642ec226SSepherosa Ziehau 	sc->hn_rss_hash = (sc->hn_rss_hcap & NDIS_HASH_FUNCTION_MASK) |
1644642ec226SSepherosa Ziehau 	    hn_rss_type_tondis(my_types);
1645642ec226SSepherosa Ziehau 	memcpy(sc->hn_rss.rss_key, ifrk.ifrk_key, sizeof(sc->hn_rss.rss_key));
1646642ec226SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
1647642ec226SSepherosa Ziehau 
1648642ec226SSepherosa Ziehau 	if (reconf) {
1649642ec226SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
1650642ec226SSepherosa Ziehau 		if (error) {
1651642ec226SSepherosa Ziehau 			/* XXX roll-back? */
1652642ec226SSepherosa Ziehau 			if_printf(ifp, "hn_rss_reconfig failed: %d\n", error);
1653642ec226SSepherosa Ziehau 			/* XXX keep going. */
1654642ec226SSepherosa Ziehau 		}
1655642ec226SSepherosa Ziehau 	}
1656642ec226SSepherosa Ziehau done:
1657642ec226SSepherosa Ziehau 	/* Hash deliverability for mbufs. */
1658642ec226SSepherosa Ziehau 	hn_rss_mbuf_hash(sc, hn_rss_type_tondis(mbuf_types));
1659642ec226SSepherosa Ziehau }
1660642ec226SSepherosa Ziehau 
1661642ec226SSepherosa Ziehau static void
1662642ec226SSepherosa Ziehau hn_vf_rss_restore(struct hn_softc *sc)
1663642ec226SSepherosa Ziehau {
1664642ec226SSepherosa Ziehau 
1665642ec226SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1666642ec226SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
1667642ec226SSepherosa Ziehau 	    ("%s: synthetic parts are not attached", sc->hn_ifp->if_xname));
1668642ec226SSepherosa Ziehau 
1669642ec226SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1)
1670642ec226SSepherosa Ziehau 		goto done;
1671642ec226SSepherosa Ziehau 
1672642ec226SSepherosa Ziehau 	/*
1673642ec226SSepherosa Ziehau 	 * Restore hash types.  Key does _not_ matter.
1674642ec226SSepherosa Ziehau 	 */
1675642ec226SSepherosa Ziehau 	if (sc->hn_rss_hash != sc->hn_rss_hcap) {
1676642ec226SSepherosa Ziehau 		int error;
1677642ec226SSepherosa Ziehau 
1678642ec226SSepherosa Ziehau 		sc->hn_rss_hash = sc->hn_rss_hcap;
1679642ec226SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
1680642ec226SSepherosa Ziehau 		if (error) {
1681642ec226SSepherosa Ziehau 			if_printf(sc->hn_ifp, "hn_rss_reconfig failed: %d\n",
1682642ec226SSepherosa Ziehau 			    error);
1683642ec226SSepherosa Ziehau 			/* XXX keep going. */
1684642ec226SSepherosa Ziehau 		}
1685642ec226SSepherosa Ziehau 	}
1686642ec226SSepherosa Ziehau done:
1687642ec226SSepherosa Ziehau 	/* Hash deliverability for mbufs. */
1688642ec226SSepherosa Ziehau 	hn_rss_mbuf_hash(sc, NDIS_HASH_ALL);
1689642ec226SSepherosa Ziehau }
1690642ec226SSepherosa Ziehau 
16919c6cae24SSepherosa Ziehau static void
16929c6cae24SSepherosa Ziehau hn_xpnt_vf_setready(struct hn_softc *sc)
16939c6cae24SSepherosa Ziehau {
16949c6cae24SSepherosa Ziehau 	struct ifnet *ifp, *vf_ifp;
16959c6cae24SSepherosa Ziehau 	struct ifreq ifr;
16969c6cae24SSepherosa Ziehau 
16979c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
16989c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
16999c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
17009c6cae24SSepherosa Ziehau 
17019c6cae24SSepherosa Ziehau 	/*
17029c6cae24SSepherosa Ziehau 	 * Mark the VF ready.
17039c6cae24SSepherosa Ziehau 	 */
17049c6cae24SSepherosa Ziehau 	sc->hn_vf_rdytick = 0;
17059c6cae24SSepherosa Ziehau 
17069c6cae24SSepherosa Ziehau 	/*
17079c6cae24SSepherosa Ziehau 	 * Save information for restoration.
17089c6cae24SSepherosa Ziehau 	 */
17099c6cae24SSepherosa Ziehau 	sc->hn_saved_caps = ifp->if_capabilities;
17109c6cae24SSepherosa Ziehau 	sc->hn_saved_tsomax = ifp->if_hw_tsomax;
17119c6cae24SSepherosa Ziehau 	sc->hn_saved_tsosegcnt = ifp->if_hw_tsomaxsegcount;
17129c6cae24SSepherosa Ziehau 	sc->hn_saved_tsosegsz = ifp->if_hw_tsomaxsegsize;
17139c6cae24SSepherosa Ziehau 
17149c6cae24SSepherosa Ziehau 	/*
17159c6cae24SSepherosa Ziehau 	 * Intersect supported/enabled capabilities.
17169c6cae24SSepherosa Ziehau 	 *
17179c6cae24SSepherosa Ziehau 	 * NOTE:
17189c6cae24SSepherosa Ziehau 	 * if_hwassist is not changed here.
17199c6cae24SSepherosa Ziehau 	 */
17209c6cae24SSepherosa Ziehau 	ifp->if_capabilities &= vf_ifp->if_capabilities;
17219c6cae24SSepherosa Ziehau 	ifp->if_capenable &= ifp->if_capabilities;
17229c6cae24SSepherosa Ziehau 
17239c6cae24SSepherosa Ziehau 	/*
17249c6cae24SSepherosa Ziehau 	 * Fix TSO settings.
17259c6cae24SSepherosa Ziehau 	 */
17269c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomax > vf_ifp->if_hw_tsomax)
17279c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomax = vf_ifp->if_hw_tsomax;
17289c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomaxsegcount > vf_ifp->if_hw_tsomaxsegcount)
17299c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomaxsegcount = vf_ifp->if_hw_tsomaxsegcount;
17309c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomaxsegsize > vf_ifp->if_hw_tsomaxsegsize)
17319c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomaxsegsize = vf_ifp->if_hw_tsomaxsegsize;
17329c6cae24SSepherosa Ziehau 
17339c6cae24SSepherosa Ziehau 	/*
17349c6cae24SSepherosa Ziehau 	 * Change VF's enabled capabilities.
17359c6cae24SSepherosa Ziehau 	 */
17369c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
17379c6cae24SSepherosa Ziehau 	strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
17389c6cae24SSepherosa Ziehau 	ifr.ifr_reqcap = ifp->if_capenable;
17399c6cae24SSepherosa Ziehau 	hn_xpnt_vf_iocsetcaps(sc, &ifr);
17409c6cae24SSepherosa Ziehau 
17419c6cae24SSepherosa Ziehau 	if (ifp->if_mtu != ETHERMTU) {
17429c6cae24SSepherosa Ziehau 		int error;
17439c6cae24SSepherosa Ziehau 
17449c6cae24SSepherosa Ziehau 		/*
17459c6cae24SSepherosa Ziehau 		 * Change VF's MTU.
17469c6cae24SSepherosa Ziehau 		 */
17479c6cae24SSepherosa Ziehau 		memset(&ifr, 0, sizeof(ifr));
17489c6cae24SSepherosa Ziehau 		strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
17499c6cae24SSepherosa Ziehau 		ifr.ifr_mtu = ifp->if_mtu;
17509c6cae24SSepherosa Ziehau 		error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFMTU, (caddr_t)&ifr);
17519c6cae24SSepherosa Ziehau 		if (error) {
17529c6cae24SSepherosa Ziehau 			if_printf(ifp, "%s SIOCSIFMTU %u failed\n",
17539c6cae24SSepherosa Ziehau 			    vf_ifp->if_xname, ifp->if_mtu);
17549c6cae24SSepherosa Ziehau 			if (ifp->if_mtu > ETHERMTU) {
17559c6cae24SSepherosa Ziehau 				if_printf(ifp, "change MTU to %d\n", ETHERMTU);
17569c6cae24SSepherosa Ziehau 
17579c6cae24SSepherosa Ziehau 				/*
17589c6cae24SSepherosa Ziehau 				 * XXX
17599c6cae24SSepherosa Ziehau 				 * No need to adjust the synthetic parts' MTU;
17609c6cae24SSepherosa Ziehau 				 * failure of the adjustment will cause us
17619c6cae24SSepherosa Ziehau 				 * infinite headache.
17629c6cae24SSepherosa Ziehau 				 */
17639c6cae24SSepherosa Ziehau 				ifp->if_mtu = ETHERMTU;
17649c6cae24SSepherosa Ziehau 				hn_mtu_change_fixup(sc);
17659c6cae24SSepherosa Ziehau 			}
17669c6cae24SSepherosa Ziehau 		}
17679c6cae24SSepherosa Ziehau 	}
17689c6cae24SSepherosa Ziehau }
17699c6cae24SSepherosa Ziehau 
17709c6cae24SSepherosa Ziehau static bool
17719c6cae24SSepherosa Ziehau hn_xpnt_vf_isready(struct hn_softc *sc)
17729c6cae24SSepherosa Ziehau {
17739c6cae24SSepherosa Ziehau 
17749c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
17759c6cae24SSepherosa Ziehau 
17769c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf || sc->hn_vf_ifp == NULL)
17779c6cae24SSepherosa Ziehau 		return (false);
17789c6cae24SSepherosa Ziehau 
17799c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick == 0)
17809c6cae24SSepherosa Ziehau 		return (true);
17819c6cae24SSepherosa Ziehau 
17829c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick > ticks)
17839c6cae24SSepherosa Ziehau 		return (false);
17849c6cae24SSepherosa Ziehau 
17859c6cae24SSepherosa Ziehau 	/* Mark VF as ready. */
17869c6cae24SSepherosa Ziehau 	hn_xpnt_vf_setready(sc);
17879c6cae24SSepherosa Ziehau 	return (true);
17889c6cae24SSepherosa Ziehau }
17899c6cae24SSepherosa Ziehau 
17909c6cae24SSepherosa Ziehau static void
1791a97fff19SSepherosa Ziehau hn_xpnt_vf_setenable(struct hn_softc *sc)
1792a97fff19SSepherosa Ziehau {
1793a97fff19SSepherosa Ziehau 	int i;
1794a97fff19SSepherosa Ziehau 
1795a97fff19SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1796a97fff19SSepherosa Ziehau 
1797a97fff19SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
1798a97fff19SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
1799a97fff19SSepherosa Ziehau 	sc->hn_xvf_flags |= HN_XVFFLAG_ENABLED;
1800a97fff19SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1801a97fff19SSepherosa Ziehau 
1802a97fff19SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1803a97fff19SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_XPNT_VF;
1804a97fff19SSepherosa Ziehau }
1805a97fff19SSepherosa Ziehau 
1806a97fff19SSepherosa Ziehau static void
1807a97fff19SSepherosa Ziehau hn_xpnt_vf_setdisable(struct hn_softc *sc, bool clear_vf)
1808a97fff19SSepherosa Ziehau {
1809a97fff19SSepherosa Ziehau 	int i;
1810a97fff19SSepherosa Ziehau 
1811a97fff19SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
1812a97fff19SSepherosa Ziehau 
1813a97fff19SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
1814a97fff19SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
1815a97fff19SSepherosa Ziehau 	sc->hn_xvf_flags &= ~HN_XVFFLAG_ENABLED;
1816a97fff19SSepherosa Ziehau 	if (clear_vf)
1817a97fff19SSepherosa Ziehau 		sc->hn_vf_ifp = NULL;
1818a97fff19SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1819a97fff19SSepherosa Ziehau 
1820a97fff19SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
1821a97fff19SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_rx_flags &= ~HN_RX_FLAG_XPNT_VF;
1822a97fff19SSepherosa Ziehau }
1823a97fff19SSepherosa Ziehau 
1824a97fff19SSepherosa Ziehau static void
18259c6cae24SSepherosa Ziehau hn_xpnt_vf_init(struct hn_softc *sc)
18269c6cae24SSepherosa Ziehau {
18279c6cae24SSepherosa Ziehau 	int error;
18289c6cae24SSepherosa Ziehau 
18299c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
18309c6cae24SSepherosa Ziehau 
18319c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
18329c6cae24SSepherosa Ziehau 	    ("%s: transparent VF was enabled", sc->hn_ifp->if_xname));
18339c6cae24SSepherosa Ziehau 
18349c6cae24SSepherosa Ziehau 	if (bootverbose) {
18359c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "try bringing up %s\n",
18369c6cae24SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname);
18379c6cae24SSepherosa Ziehau 	}
18389c6cae24SSepherosa Ziehau 
18399c6cae24SSepherosa Ziehau 	/*
18409c6cae24SSepherosa Ziehau 	 * Bring the VF up.
18419c6cae24SSepherosa Ziehau 	 */
18429c6cae24SSepherosa Ziehau 	hn_xpnt_vf_saveifflags(sc);
18439c6cae24SSepherosa Ziehau 	sc->hn_vf_ifp->if_flags |= IFF_UP;
18449c6cae24SSepherosa Ziehau 	error = hn_xpnt_vf_iocsetflags(sc);
18459c6cae24SSepherosa Ziehau 	if (error) {
18469c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "bringing up %s failed: %d\n",
18479c6cae24SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname, error);
18489c6cae24SSepherosa Ziehau 		return;
18499c6cae24SSepherosa Ziehau 	}
18509c6cae24SSepherosa Ziehau 
18519c6cae24SSepherosa Ziehau 	/*
18529c6cae24SSepherosa Ziehau 	 * NOTE:
18539c6cae24SSepherosa Ziehau 	 * Datapath setting must happen _after_ bringing the VF up.
18549c6cae24SSepherosa Ziehau 	 */
18559c6cae24SSepherosa Ziehau 	hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
18569c6cae24SSepherosa Ziehau 
1857642ec226SSepherosa Ziehau 	/*
1858642ec226SSepherosa Ziehau 	 * NOTE:
1859642ec226SSepherosa Ziehau 	 * Fixup RSS related bits _after_ the VF is brought up, since
1860642ec226SSepherosa Ziehau 	 * many VFs generate RSS key during it's initialization.
1861642ec226SSepherosa Ziehau 	 */
1862642ec226SSepherosa Ziehau 	hn_vf_rss_fixup(sc, true);
1863642ec226SSepherosa Ziehau 
1864a97fff19SSepherosa Ziehau 	/* Mark transparent mode VF as enabled. */
1865a97fff19SSepherosa Ziehau 	hn_xpnt_vf_setenable(sc);
18669c6cae24SSepherosa Ziehau }
18679c6cae24SSepherosa Ziehau 
18689c6cae24SSepherosa Ziehau static void
18699c6cae24SSepherosa Ziehau hn_xpnt_vf_init_taskfunc(void *xsc, int pending __unused)
18709c6cae24SSepherosa Ziehau {
18719c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
18729c6cae24SSepherosa Ziehau 
18739c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
18749c6cae24SSepherosa Ziehau 
18759c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
18769c6cae24SSepherosa Ziehau 		goto done;
18779c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
18789c6cae24SSepherosa Ziehau 		goto done;
18799c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
18809c6cae24SSepherosa Ziehau 		goto done;
18819c6cae24SSepherosa Ziehau 
18829c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick != 0) {
18839c6cae24SSepherosa Ziehau 		/* Mark VF as ready. */
18849c6cae24SSepherosa Ziehau 		hn_xpnt_vf_setready(sc);
18859c6cae24SSepherosa Ziehau 	}
18869c6cae24SSepherosa Ziehau 
18879c6cae24SSepherosa Ziehau 	if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) {
18889c6cae24SSepherosa Ziehau 		/*
18899c6cae24SSepherosa Ziehau 		 * Delayed VF initialization.
18909c6cae24SSepherosa Ziehau 		 */
18919c6cae24SSepherosa Ziehau 		if (bootverbose) {
18929c6cae24SSepherosa Ziehau 			if_printf(sc->hn_ifp, "delayed initialize %s\n",
18939c6cae24SSepherosa Ziehau 			    sc->hn_vf_ifp->if_xname);
18949c6cae24SSepherosa Ziehau 		}
18959c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
18969c6cae24SSepherosa Ziehau 	}
18979c6cae24SSepherosa Ziehau done:
18989c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
18999c6cae24SSepherosa Ziehau }
19009c6cae24SSepherosa Ziehau 
1901499c3e17SSepherosa Ziehau static void
1902499c3e17SSepherosa Ziehau hn_ifnet_attevent(void *xsc, struct ifnet *ifp)
1903499c3e17SSepherosa Ziehau {
1904499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1905499c3e17SSepherosa Ziehau 
1906499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1907499c3e17SSepherosa Ziehau 
1908499c3e17SSepherosa Ziehau 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
1909499c3e17SSepherosa Ziehau 		goto done;
1910499c3e17SSepherosa Ziehau 
1911499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1912499c3e17SSepherosa Ziehau 		goto done;
1913499c3e17SSepherosa Ziehau 
1914499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp != NULL) {
1915499c3e17SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s was attached as VF\n",
1916499c3e17SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname);
1917499c3e17SSepherosa Ziehau 		goto done;
1918499c3e17SSepherosa Ziehau 	}
1919499c3e17SSepherosa Ziehau 
19209c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && ifp->if_start != NULL) {
19219c6cae24SSepherosa Ziehau 		/*
19229c6cae24SSepherosa Ziehau 		 * ifnet.if_start is _not_ supported by transparent
19239c6cae24SSepherosa Ziehau 		 * mode VF; mainly due to the IFF_DRV_OACTIVE flag.
19249c6cae24SSepherosa Ziehau 		 */
19259c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s uses if_start, which is unsupported "
19269c6cae24SSepherosa Ziehau 		    "in transparent VF mode.\n", ifp->if_xname);
19279c6cae24SSepherosa Ziehau 		goto done;
19289c6cae24SSepherosa Ziehau 	}
19299c6cae24SSepherosa Ziehau 
1930499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
1931499c3e17SSepherosa Ziehau 
1932499c3e17SSepherosa Ziehau 	if (ifp->if_index >= hn_vfmap_size) {
1933499c3e17SSepherosa Ziehau 		struct ifnet **newmap;
1934499c3e17SSepherosa Ziehau 		int newsize;
1935499c3e17SSepherosa Ziehau 
1936499c3e17SSepherosa Ziehau 		newsize = ifp->if_index + HN_VFMAP_SIZE_DEF;
1937499c3e17SSepherosa Ziehau 		newmap = malloc(sizeof(struct ifnet *) * newsize, M_DEVBUF,
1938499c3e17SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
1939499c3e17SSepherosa Ziehau 
1940499c3e17SSepherosa Ziehau 		memcpy(newmap, hn_vfmap,
1941499c3e17SSepherosa Ziehau 		    sizeof(struct ifnet *) * hn_vfmap_size);
1942499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
1943499c3e17SSepherosa Ziehau 		hn_vfmap = newmap;
1944499c3e17SSepherosa Ziehau 		hn_vfmap_size = newsize;
1945499c3e17SSepherosa Ziehau 	}
1946499c3e17SSepherosa Ziehau 	KASSERT(hn_vfmap[ifp->if_index] == NULL,
1947499c3e17SSepherosa Ziehau 	    ("%s: ifindex %d was mapped to %s",
1948499c3e17SSepherosa Ziehau 	     ifp->if_xname, ifp->if_index, hn_vfmap[ifp->if_index]->if_xname));
1949499c3e17SSepherosa Ziehau 	hn_vfmap[ifp->if_index] = sc->hn_ifp;
1950499c3e17SSepherosa Ziehau 
1951499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
1952499c3e17SSepherosa Ziehau 
19539c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
19549c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
19559c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
19569c6cae24SSepherosa Ziehau 	    ("%s: transparent VF was enabled", sc->hn_ifp->if_xname));
1957499c3e17SSepherosa Ziehau 	sc->hn_vf_ifp = ifp;
19589c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
19599c6cae24SSepherosa Ziehau 
19609c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
19619c6cae24SSepherosa Ziehau 		int wait_ticks;
19629c6cae24SSepherosa Ziehau 
19639c6cae24SSepherosa Ziehau 		/*
19649c6cae24SSepherosa Ziehau 		 * Install if_input for vf_ifp, which does vf_ifp -> hn_ifp.
19659c6cae24SSepherosa Ziehau 		 * Save vf_ifp's current if_input for later restoration.
19669c6cae24SSepherosa Ziehau 		 */
19679c6cae24SSepherosa Ziehau 		sc->hn_vf_input = ifp->if_input;
19689c6cae24SSepherosa Ziehau 		ifp->if_input = hn_xpnt_vf_input;
19699c6cae24SSepherosa Ziehau 
19709c6cae24SSepherosa Ziehau 		/*
19719c6cae24SSepherosa Ziehau 		 * Stop link status management; use the VF's.
19729c6cae24SSepherosa Ziehau 		 */
19739c6cae24SSepherosa Ziehau 		hn_suspend_mgmt(sc);
19749c6cae24SSepherosa Ziehau 
19759c6cae24SSepherosa Ziehau 		/*
19769c6cae24SSepherosa Ziehau 		 * Give VF sometime to complete its attach routing.
19779c6cae24SSepherosa Ziehau 		 */
19789c6cae24SSepherosa Ziehau 		wait_ticks = hn_xpnt_vf_attwait * hz;
19799c6cae24SSepherosa Ziehau 		sc->hn_vf_rdytick = ticks + wait_ticks;
19809c6cae24SSepherosa Ziehau 
19819c6cae24SSepherosa Ziehau 		taskqueue_enqueue_timeout(sc->hn_vf_taskq, &sc->hn_vf_init,
19829c6cae24SSepherosa Ziehau 		    wait_ticks);
19839c6cae24SSepherosa Ziehau 	}
1984499c3e17SSepherosa Ziehau done:
1985499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
1986499c3e17SSepherosa Ziehau }
1987499c3e17SSepherosa Ziehau 
1988499c3e17SSepherosa Ziehau static void
1989499c3e17SSepherosa Ziehau hn_ifnet_detevent(void *xsc, struct ifnet *ifp)
1990499c3e17SSepherosa Ziehau {
1991499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1992499c3e17SSepherosa Ziehau 
1993499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1994499c3e17SSepherosa Ziehau 
1995499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
1996499c3e17SSepherosa Ziehau 		goto done;
1997499c3e17SSepherosa Ziehau 
1998499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1999499c3e17SSepherosa Ziehau 		goto done;
2000499c3e17SSepherosa Ziehau 
20019c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
20029c6cae24SSepherosa Ziehau 		/*
20039c6cae24SSepherosa Ziehau 		 * Make sure that the delayed initialization is not running.
20049c6cae24SSepherosa Ziehau 		 *
20059c6cae24SSepherosa Ziehau 		 * NOTE:
20069c6cae24SSepherosa Ziehau 		 * - This lock _must_ be released, since the hn_vf_init task
20079c6cae24SSepherosa Ziehau 		 *   will try holding this lock.
20089c6cae24SSepherosa Ziehau 		 * - It is safe to release this lock here, since the
20099c6cae24SSepherosa Ziehau 		 *   hn_ifnet_attevent() is interlocked by the hn_vf_ifp.
20109c6cae24SSepherosa Ziehau 		 *
20119c6cae24SSepherosa Ziehau 		 * XXX racy, if hn(4) ever detached.
20129c6cae24SSepherosa Ziehau 		 */
20139c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
20149c6cae24SSepherosa Ziehau 		taskqueue_drain_timeout(sc->hn_vf_taskq, &sc->hn_vf_init);
20159c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
20169c6cae24SSepherosa Ziehau 
20179c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_input != NULL, ("%s VF input is not saved",
20189c6cae24SSepherosa Ziehau 		    sc->hn_ifp->if_xname));
20199c6cae24SSepherosa Ziehau 		ifp->if_input = sc->hn_vf_input;
20209c6cae24SSepherosa Ziehau 		sc->hn_vf_input = NULL;
20219c6cae24SSepherosa Ziehau 
2022642ec226SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) &&
2023642ec226SSepherosa Ziehau 		    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED))
20249c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
20259c6cae24SSepherosa Ziehau 
20269c6cae24SSepherosa Ziehau 		if (sc->hn_vf_rdytick == 0) {
20279c6cae24SSepherosa Ziehau 			/*
20289c6cae24SSepherosa Ziehau 			 * The VF was ready; restore some settings.
20299c6cae24SSepherosa Ziehau 			 */
20309c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_capabilities = sc->hn_saved_caps;
20319c6cae24SSepherosa Ziehau 			/*
20329c6cae24SSepherosa Ziehau 			 * NOTE:
20339c6cae24SSepherosa Ziehau 			 * There is _no_ need to fixup if_capenable and
20349c6cae24SSepherosa Ziehau 			 * if_hwassist, since the if_capabilities before
20359c6cae24SSepherosa Ziehau 			 * restoration was an intersection of the VF's
20369c6cae24SSepherosa Ziehau 			 * if_capabilites and the synthetic device's
20379c6cae24SSepherosa Ziehau 			 * if_capabilites.
20389c6cae24SSepherosa Ziehau 			 */
20399c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomax = sc->hn_saved_tsomax;
20409c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomaxsegcount =
20419c6cae24SSepherosa Ziehau 			    sc->hn_saved_tsosegcnt;
20429c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomaxsegsize = sc->hn_saved_tsosegsz;
20439c6cae24SSepherosa Ziehau 		}
20449c6cae24SSepherosa Ziehau 
2045642ec226SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
2046642ec226SSepherosa Ziehau 			/*
2047642ec226SSepherosa Ziehau 			 * Restore RSS settings.
2048642ec226SSepherosa Ziehau 			 */
2049642ec226SSepherosa Ziehau 			hn_vf_rss_restore(sc);
2050642ec226SSepherosa Ziehau 
20519c6cae24SSepherosa Ziehau 			/*
20529c6cae24SSepherosa Ziehau 			 * Resume link status management, which was suspended
20539c6cae24SSepherosa Ziehau 			 * by hn_ifnet_attevent().
20549c6cae24SSepherosa Ziehau 			 */
20559c6cae24SSepherosa Ziehau 			hn_resume_mgmt(sc);
20569c6cae24SSepherosa Ziehau 		}
2057642ec226SSepherosa Ziehau 	}
20589c6cae24SSepherosa Ziehau 
2059a97fff19SSepherosa Ziehau 	/* Mark transparent mode VF as disabled. */
2060a97fff19SSepherosa Ziehau 	hn_xpnt_vf_setdisable(sc, true /* clear hn_vf_ifp */);
2061499c3e17SSepherosa Ziehau 
2062499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
2063499c3e17SSepherosa Ziehau 
2064499c3e17SSepherosa Ziehau 	KASSERT(ifp->if_index < hn_vfmap_size,
2065499c3e17SSepherosa Ziehau 	    ("ifindex %d, vfmapsize %d", ifp->if_index, hn_vfmap_size));
2066499c3e17SSepherosa Ziehau 	if (hn_vfmap[ifp->if_index] != NULL) {
2067499c3e17SSepherosa Ziehau 		KASSERT(hn_vfmap[ifp->if_index] == sc->hn_ifp,
2068499c3e17SSepherosa Ziehau 		    ("%s: ifindex %d was mapped to %s",
2069499c3e17SSepherosa Ziehau 		     ifp->if_xname, ifp->if_index,
2070499c3e17SSepherosa Ziehau 		     hn_vfmap[ifp->if_index]->if_xname));
2071499c3e17SSepherosa Ziehau 		hn_vfmap[ifp->if_index] = NULL;
2072499c3e17SSepherosa Ziehau 	}
2073499c3e17SSepherosa Ziehau 
2074499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
2075499c3e17SSepherosa Ziehau done:
2076499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
2077499c3e17SSepherosa Ziehau }
2078499c3e17SSepherosa Ziehau 
20799c6cae24SSepherosa Ziehau static void
20809c6cae24SSepherosa Ziehau hn_ifnet_lnkevent(void *xsc, struct ifnet *ifp, int link_state)
20819c6cae24SSepherosa Ziehau {
20829c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
20839c6cae24SSepherosa Ziehau 
20849c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == ifp)
20859c6cae24SSepherosa Ziehau 		if_link_state_change(sc->hn_ifp, link_state);
20869c6cae24SSepherosa Ziehau }
20879c6cae24SSepherosa Ziehau 
208815516c77SSepherosa Ziehau static int
208915516c77SSepherosa Ziehau hn_probe(device_t dev)
209015516c77SSepherosa Ziehau {
209115516c77SSepherosa Ziehau 
2092c2d50b26SSepherosa Ziehau 	if (VMBUS_PROBE_GUID(device_get_parent(dev), dev, &hn_guid) == 0) {
209315516c77SSepherosa Ziehau 		device_set_desc(dev, "Hyper-V Network Interface");
209415516c77SSepherosa Ziehau 		return BUS_PROBE_DEFAULT;
209515516c77SSepherosa Ziehau 	}
209615516c77SSepherosa Ziehau 	return ENXIO;
209715516c77SSepherosa Ziehau }
209815516c77SSepherosa Ziehau 
209915516c77SSepherosa Ziehau static int
210015516c77SSepherosa Ziehau hn_attach(device_t dev)
210115516c77SSepherosa Ziehau {
210215516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
210315516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
210415516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
210515516c77SSepherosa Ziehau 	uint8_t eaddr[ETHER_ADDR_LEN];
210615516c77SSepherosa Ziehau 	struct ifnet *ifp = NULL;
210715516c77SSepherosa Ziehau 	int error, ring_cnt, tx_ring_cnt;
2108eb2fe044SSepherosa Ziehau 	uint32_t mtu;
210915516c77SSepherosa Ziehau 
211015516c77SSepherosa Ziehau 	sc->hn_dev = dev;
211115516c77SSepherosa Ziehau 	sc->hn_prichan = vmbus_get_channel(dev);
211215516c77SSepherosa Ziehau 	HN_LOCK_INIT(sc);
21139c6cae24SSepherosa Ziehau 	rm_init(&sc->hn_vf_lock, "hnvf");
21149c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_xpnt_vf_accbpf)
21159c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
211615516c77SSepherosa Ziehau 
211715516c77SSepherosa Ziehau 	/*
2118dc13fee6SSepherosa Ziehau 	 * Initialize these tunables once.
2119dc13fee6SSepherosa Ziehau 	 */
2120dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = hn_tx_agg_size;
2121dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = hn_tx_agg_pkts;
2122dc13fee6SSepherosa Ziehau 
2123dc13fee6SSepherosa Ziehau 	/*
212415516c77SSepherosa Ziehau 	 * Setup taskqueue for transmission.
212515516c77SSepherosa Ziehau 	 */
21260e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_INDEP) {
2127fdd0222aSSepherosa Ziehau 		int i;
2128fdd0222aSSepherosa Ziehau 
2129fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs =
2130fdd0222aSSepherosa Ziehau 		    malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
2131fdd0222aSSepherosa Ziehau 		    M_DEVBUF, M_WAITOK);
2132fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i) {
2133fdd0222aSSepherosa Ziehau 			sc->hn_tx_taskqs[i] = taskqueue_create("hn_tx",
2134fdd0222aSSepherosa Ziehau 			    M_WAITOK, taskqueue_thread_enqueue,
2135fdd0222aSSepherosa Ziehau 			    &sc->hn_tx_taskqs[i]);
2136fdd0222aSSepherosa Ziehau 			taskqueue_start_threads(&sc->hn_tx_taskqs[i], 1, PI_NET,
2137fdd0222aSSepherosa Ziehau 			    "%s tx%d", device_get_nameunit(dev), i);
2138fdd0222aSSepherosa Ziehau 		}
21390e11868dSSepherosa Ziehau 	} else if (hn_tx_taskq_mode == HN_TX_TASKQ_M_GLOBAL) {
2140fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs = hn_tx_taskque;
214115516c77SSepherosa Ziehau 	}
214215516c77SSepherosa Ziehau 
214315516c77SSepherosa Ziehau 	/*
214415516c77SSepherosa Ziehau 	 * Setup taskqueue for mangement tasks, e.g. link status.
214515516c77SSepherosa Ziehau 	 */
214615516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq0 = taskqueue_create("hn_mgmt", M_WAITOK,
214715516c77SSepherosa Ziehau 	    taskqueue_thread_enqueue, &sc->hn_mgmt_taskq0);
214815516c77SSepherosa Ziehau 	taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
214915516c77SSepherosa Ziehau 	    device_get_nameunit(dev));
215015516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc);
215115516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_netchg_init, 0, hn_netchg_init_taskfunc, sc);
215215516c77SSepherosa Ziehau 	TIMEOUT_TASK_INIT(sc->hn_mgmt_taskq0, &sc->hn_netchg_status, 0,
215315516c77SSepherosa Ziehau 	    hn_netchg_status_taskfunc, sc);
215415516c77SSepherosa Ziehau 
21559c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
21569c6cae24SSepherosa Ziehau 		/*
21579c6cae24SSepherosa Ziehau 		 * Setup taskqueue for VF tasks, e.g. delayed VF bringing up.
21589c6cae24SSepherosa Ziehau 		 */
21599c6cae24SSepherosa Ziehau 		sc->hn_vf_taskq = taskqueue_create("hn_vf", M_WAITOK,
21609c6cae24SSepherosa Ziehau 		    taskqueue_thread_enqueue, &sc->hn_vf_taskq);
21619c6cae24SSepherosa Ziehau 		taskqueue_start_threads(&sc->hn_vf_taskq, 1, PI_NET, "%s vf",
21629c6cae24SSepherosa Ziehau 		    device_get_nameunit(dev));
21639c6cae24SSepherosa Ziehau 		TIMEOUT_TASK_INIT(sc->hn_vf_taskq, &sc->hn_vf_init, 0,
21649c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_init_taskfunc, sc);
21659c6cae24SSepherosa Ziehau 	}
21669c6cae24SSepherosa Ziehau 
216715516c77SSepherosa Ziehau 	/*
216815516c77SSepherosa Ziehau 	 * Allocate ifnet and setup its name earlier, so that if_printf
216915516c77SSepherosa Ziehau 	 * can be used by functions, which will be called after
217015516c77SSepherosa Ziehau 	 * ether_ifattach().
217115516c77SSepherosa Ziehau 	 */
217215516c77SSepherosa Ziehau 	ifp = sc->hn_ifp = if_alloc(IFT_ETHER);
217315516c77SSepherosa Ziehau 	ifp->if_softc = sc;
217415516c77SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
217515516c77SSepherosa Ziehau 
217615516c77SSepherosa Ziehau 	/*
217715516c77SSepherosa Ziehau 	 * Initialize ifmedia earlier so that it can be unconditionally
217815516c77SSepherosa Ziehau 	 * destroyed, if error happened later on.
217915516c77SSepherosa Ziehau 	 */
218015516c77SSepherosa Ziehau 	ifmedia_init(&sc->hn_media, 0, hn_ifmedia_upd, hn_ifmedia_sts);
218115516c77SSepherosa Ziehau 
218215516c77SSepherosa Ziehau 	/*
218315516c77SSepherosa Ziehau 	 * Figure out the # of RX rings (ring_cnt) and the # of TX rings
218415516c77SSepherosa Ziehau 	 * to use (tx_ring_cnt).
218515516c77SSepherosa Ziehau 	 *
218615516c77SSepherosa Ziehau 	 * NOTE:
218715516c77SSepherosa Ziehau 	 * The # of RX rings to use is same as the # of channels to use.
218815516c77SSepherosa Ziehau 	 */
218915516c77SSepherosa Ziehau 	ring_cnt = hn_chan_cnt;
219015516c77SSepherosa Ziehau 	if (ring_cnt <= 0) {
219115516c77SSepherosa Ziehau 		/* Default */
219215516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
219315516c77SSepherosa Ziehau 		if (ring_cnt > HN_RING_CNT_DEF_MAX)
219415516c77SSepherosa Ziehau 			ring_cnt = HN_RING_CNT_DEF_MAX;
219515516c77SSepherosa Ziehau 	} else if (ring_cnt > mp_ncpus) {
219615516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
219715516c77SSepherosa Ziehau 	}
219834d68912SSepherosa Ziehau #ifdef RSS
219934d68912SSepherosa Ziehau 	if (ring_cnt > rss_getnumbuckets())
220034d68912SSepherosa Ziehau 		ring_cnt = rss_getnumbuckets();
220134d68912SSepherosa Ziehau #endif
220215516c77SSepherosa Ziehau 
220315516c77SSepherosa Ziehau 	tx_ring_cnt = hn_tx_ring_cnt;
220415516c77SSepherosa Ziehau 	if (tx_ring_cnt <= 0 || tx_ring_cnt > ring_cnt)
220515516c77SSepherosa Ziehau 		tx_ring_cnt = ring_cnt;
220623bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
220715516c77SSepherosa Ziehau 	if (hn_use_if_start) {
220815516c77SSepherosa Ziehau 		/* ifnet.if_start only needs one TX ring. */
220915516c77SSepherosa Ziehau 		tx_ring_cnt = 1;
221015516c77SSepherosa Ziehau 	}
221123bf9e15SSepherosa Ziehau #endif
221215516c77SSepherosa Ziehau 
221315516c77SSepherosa Ziehau 	/*
221415516c77SSepherosa Ziehau 	 * Set the leader CPU for channels.
221515516c77SSepherosa Ziehau 	 */
221615516c77SSepherosa Ziehau 	sc->hn_cpu = atomic_fetchadd_int(&hn_cpu_index, ring_cnt) % mp_ncpus;
221715516c77SSepherosa Ziehau 
221815516c77SSepherosa Ziehau 	/*
221915516c77SSepherosa Ziehau 	 * Create enough TX/RX rings, even if only limited number of
222015516c77SSepherosa Ziehau 	 * channels can be allocated.
222115516c77SSepherosa Ziehau 	 */
222215516c77SSepherosa Ziehau 	error = hn_create_tx_data(sc, tx_ring_cnt);
222315516c77SSepherosa Ziehau 	if (error)
222415516c77SSepherosa Ziehau 		goto failed;
222515516c77SSepherosa Ziehau 	error = hn_create_rx_data(sc, ring_cnt);
222615516c77SSepherosa Ziehau 	if (error)
222715516c77SSepherosa Ziehau 		goto failed;
222815516c77SSepherosa Ziehau 
222915516c77SSepherosa Ziehau 	/*
223015516c77SSepherosa Ziehau 	 * Create transaction context for NVS and RNDIS transactions.
223115516c77SSepherosa Ziehau 	 */
223215516c77SSepherosa Ziehau 	sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
223315516c77SSepherosa Ziehau 	    HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
223425641fc7SSepherosa Ziehau 	if (sc->hn_xact == NULL) {
223525641fc7SSepherosa Ziehau 		error = ENXIO;
223615516c77SSepherosa Ziehau 		goto failed;
223725641fc7SSepherosa Ziehau 	}
223825641fc7SSepherosa Ziehau 
223925641fc7SSepherosa Ziehau 	/*
224025641fc7SSepherosa Ziehau 	 * Install orphan handler for the revocation of this device's
224125641fc7SSepherosa Ziehau 	 * primary channel.
224225641fc7SSepherosa Ziehau 	 *
224325641fc7SSepherosa Ziehau 	 * NOTE:
224425641fc7SSepherosa Ziehau 	 * The processing order is critical here:
224525641fc7SSepherosa Ziehau 	 * Install the orphan handler, _before_ testing whether this
224625641fc7SSepherosa Ziehau 	 * device's primary channel has been revoked or not.
224725641fc7SSepherosa Ziehau 	 */
224825641fc7SSepherosa Ziehau 	vmbus_chan_set_orphan(sc->hn_prichan, sc->hn_xact);
224925641fc7SSepherosa Ziehau 	if (vmbus_chan_is_revoked(sc->hn_prichan)) {
225025641fc7SSepherosa Ziehau 		error = ENXIO;
225125641fc7SSepherosa Ziehau 		goto failed;
225225641fc7SSepherosa Ziehau 	}
225315516c77SSepherosa Ziehau 
225415516c77SSepherosa Ziehau 	/*
225515516c77SSepherosa Ziehau 	 * Attach the synthetic parts, i.e. NVS and RNDIS.
225615516c77SSepherosa Ziehau 	 */
225715516c77SSepherosa Ziehau 	error = hn_synth_attach(sc, ETHERMTU);
225815516c77SSepherosa Ziehau 	if (error)
225915516c77SSepherosa Ziehau 		goto failed;
226015516c77SSepherosa Ziehau 
226115516c77SSepherosa Ziehau 	error = hn_rndis_get_eaddr(sc, eaddr);
226215516c77SSepherosa Ziehau 	if (error)
226315516c77SSepherosa Ziehau 		goto failed;
226415516c77SSepherosa Ziehau 
2265eb2fe044SSepherosa Ziehau 	error = hn_rndis_get_mtu(sc, &mtu);
2266eb2fe044SSepherosa Ziehau 	if (error)
2267eb2fe044SSepherosa Ziehau 		mtu = ETHERMTU;
2268eb2fe044SSepherosa Ziehau 	else if (bootverbose)
2269eb2fe044SSepherosa Ziehau 		device_printf(dev, "RNDIS mtu %u\n", mtu);
2270eb2fe044SSepherosa Ziehau 
227115516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
227215516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
227315516c77SSepherosa Ziehau 		/*
227415516c77SSepherosa Ziehau 		 * Reduce TCP segment aggregation limit for multiple
227515516c77SSepherosa Ziehau 		 * RX rings to increase ACK timeliness.
227615516c77SSepherosa Ziehau 		 */
227715516c77SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF);
227815516c77SSepherosa Ziehau 	}
227915516c77SSepherosa Ziehau #endif
228015516c77SSepherosa Ziehau 
228115516c77SSepherosa Ziehau 	/*
2282db76829bSSepherosa Ziehau 	 * Fixup TX/RX stuffs after synthetic parts are attached.
228315516c77SSepherosa Ziehau 	 */
228415516c77SSepherosa Ziehau 	hn_fixup_tx_data(sc);
2285db76829bSSepherosa Ziehau 	hn_fixup_rx_data(sc);
228615516c77SSepherosa Ziehau 
228715516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
228815516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
228915516c77SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "nvs_version", CTLFLAG_RD,
229015516c77SSepherosa Ziehau 	    &sc->hn_nvs_ver, 0, "NVS version");
229115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ndis_version",
229215516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
229315516c77SSepherosa Ziehau 	    hn_ndis_version_sysctl, "A", "NDIS version");
229415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "caps",
229515516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
229615516c77SSepherosa Ziehau 	    hn_caps_sysctl, "A", "capabilities");
229715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist",
229815516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
229915516c77SSepherosa Ziehau 	    hn_hwassist_sysctl, "A", "hwassist");
23009c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_max",
23019c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomax, 0, "max TSO size");
23029c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_maxsegcnt",
23039c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomaxsegcount, 0,
23049c6cae24SSepherosa Ziehau 	    "max # of TSO segments");
23059c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_maxsegsz",
23069c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomaxsegsize, 0,
23079c6cae24SSepherosa Ziehau 	    "max size of TSO segment");
230815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter",
230915516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
231015516c77SSepherosa Ziehau 	    hn_rxfilter_sysctl, "A", "rxfilter");
231115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hash",
231215516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
231315516c77SSepherosa Ziehau 	    hn_rss_hash_sysctl, "A", "RSS hash");
2314642ec226SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hashcap",
2315642ec226SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2316642ec226SSepherosa Ziehau 	    hn_rss_hcap_sysctl, "A", "RSS hash capabilities");
2317642ec226SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "mbuf_hash",
2318642ec226SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2319642ec226SSepherosa Ziehau 	    hn_rss_mbuf_sysctl, "A", "RSS hash for mbufs");
232015516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rss_ind_size",
232115516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rss_ind_size, 0, "RSS indirect entry count");
232234d68912SSepherosa Ziehau #ifndef RSS
232334d68912SSepherosa Ziehau 	/*
232434d68912SSepherosa Ziehau 	 * Don't allow RSS key/indirect table changes, if RSS is defined.
232534d68912SSepherosa Ziehau 	 */
232615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key",
232715516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
232815516c77SSepherosa Ziehau 	    hn_rss_key_sysctl, "IU", "RSS key");
232915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_ind",
233015516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
233115516c77SSepherosa Ziehau 	    hn_rss_ind_sysctl, "IU", "RSS indirect table");
233234d68912SSepherosa Ziehau #endif
2333dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_size",
2334dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_size, 0,
2335dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation size limit");
2336dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_pkts",
2337dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_pkts, 0,
2338dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation count limit");
2339dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_align",
2340dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_align, 0,
2341dc13fee6SSepherosa Ziehau 	    "RNDIS packet transmission aggregation alignment");
2342dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_size",
2343dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
2344dc13fee6SSepherosa Ziehau 	    hn_txagg_size_sysctl, "I",
2345dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation size, 0 -- disable, -1 -- auto");
2346dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pkts",
2347dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
2348dc13fee6SSepherosa Ziehau 	    hn_txagg_pkts_sysctl, "I",
2349dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation packets, "
2350dc13fee6SSepherosa Ziehau 	    "0 -- disable, -1 -- auto");
23516c1204dfSSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "polling",
23526c1204dfSSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
23536c1204dfSSepherosa Ziehau 	    hn_polling_sysctl, "I",
23546c1204dfSSepherosa Ziehau 	    "Polling frequency: [100,1000000], 0 disable polling");
235540d60d6eSDexuan Cui 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf",
235640d60d6eSDexuan Cui 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
235740d60d6eSDexuan Cui 	    hn_vf_sysctl, "A", "Virtual Function's name");
23589c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
2359499c3e17SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxvf",
2360499c3e17SSepherosa Ziehau 		    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
2361499c3e17SSepherosa Ziehau 		    hn_rxvf_sysctl, "A", "activated Virtual Function's name");
23629c6cae24SSepherosa Ziehau 	} else {
23639c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_enabled",
23649c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
23659c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_enabled_sysctl, "I",
23669c6cae24SSepherosa Ziehau 		    "Transparent VF enabled");
23679c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_accbpf",
23689c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
23699c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_accbpf_sysctl, "I",
23709c6cae24SSepherosa Ziehau 		    "Accurate BPF for transparent VF");
23719c6cae24SSepherosa Ziehau 	}
237215516c77SSepherosa Ziehau 
237315516c77SSepherosa Ziehau 	/*
237415516c77SSepherosa Ziehau 	 * Setup the ifmedia, which has been initialized earlier.
237515516c77SSepherosa Ziehau 	 */
237615516c77SSepherosa Ziehau 	ifmedia_add(&sc->hn_media, IFM_ETHER | IFM_AUTO, 0, NULL);
237715516c77SSepherosa Ziehau 	ifmedia_set(&sc->hn_media, IFM_ETHER | IFM_AUTO);
237815516c77SSepherosa Ziehau 	/* XXX ifmedia_set really should do this for us */
237915516c77SSepherosa Ziehau 	sc->hn_media.ifm_media = sc->hn_media.ifm_cur->ifm_media;
238015516c77SSepherosa Ziehau 
238115516c77SSepherosa Ziehau 	/*
238215516c77SSepherosa Ziehau 	 * Setup the ifnet for this interface.
238315516c77SSepherosa Ziehau 	 */
238415516c77SSepherosa Ziehau 
238515516c77SSepherosa Ziehau 	ifp->if_baudrate = IF_Gbps(10);
2386e87c4940SGleb Smirnoff 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
238715516c77SSepherosa Ziehau 	ifp->if_ioctl = hn_ioctl;
238815516c77SSepherosa Ziehau 	ifp->if_init = hn_init;
238923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
239015516c77SSepherosa Ziehau 	if (hn_use_if_start) {
239115516c77SSepherosa Ziehau 		int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]);
239215516c77SSepherosa Ziehau 
239315516c77SSepherosa Ziehau 		ifp->if_start = hn_start;
239415516c77SSepherosa Ziehau 		IFQ_SET_MAXLEN(&ifp->if_snd, qdepth);
239515516c77SSepherosa Ziehau 		ifp->if_snd.ifq_drv_maxlen = qdepth - 1;
239615516c77SSepherosa Ziehau 		IFQ_SET_READY(&ifp->if_snd);
239723bf9e15SSepherosa Ziehau 	} else
239823bf9e15SSepherosa Ziehau #endif
239923bf9e15SSepherosa Ziehau 	{
240015516c77SSepherosa Ziehau 		ifp->if_transmit = hn_transmit;
240115516c77SSepherosa Ziehau 		ifp->if_qflush = hn_xmit_qflush;
240215516c77SSepherosa Ziehau 	}
240315516c77SSepherosa Ziehau 
24049c6cae24SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_LRO | IFCAP_LINKSTATE;
240515516c77SSepherosa Ziehau #ifdef foo
240615516c77SSepherosa Ziehau 	/* We can't diff IPv6 packets from IPv4 packets on RX path. */
240715516c77SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_RXCSUM_IPV6;
240815516c77SSepherosa Ziehau #endif
240915516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_VLAN) {
241015516c77SSepherosa Ziehau 		/* XXX not sure about VLAN_MTU. */
241115516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
241215516c77SSepherosa Ziehau 	}
241315516c77SSepherosa Ziehau 
241415516c77SSepherosa Ziehau 	ifp->if_hwassist = sc->hn_tx_ring[0].hn_csum_assist;
241515516c77SSepherosa Ziehau 	if (ifp->if_hwassist & HN_CSUM_IP_MASK)
241615516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TXCSUM;
241715516c77SSepherosa Ziehau 	if (ifp->if_hwassist & HN_CSUM_IP6_MASK)
241815516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TXCSUM_IPV6;
241915516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO4) {
242015516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TSO4;
242115516c77SSepherosa Ziehau 		ifp->if_hwassist |= CSUM_IP_TSO;
242215516c77SSepherosa Ziehau 	}
242315516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO6) {
242415516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TSO6;
242515516c77SSepherosa Ziehau 		ifp->if_hwassist |= CSUM_IP6_TSO;
242615516c77SSepherosa Ziehau 	}
242715516c77SSepherosa Ziehau 
242815516c77SSepherosa Ziehau 	/* Enable all available capabilities by default. */
242915516c77SSepherosa Ziehau 	ifp->if_capenable = ifp->if_capabilities;
243015516c77SSepherosa Ziehau 
24317960e6baSSepherosa Ziehau 	/*
24327960e6baSSepherosa Ziehau 	 * Disable IPv6 TSO and TXCSUM by default, they still can
24337960e6baSSepherosa Ziehau 	 * be enabled through SIOCSIFCAP.
24347960e6baSSepherosa Ziehau 	 */
24357960e6baSSepherosa Ziehau 	ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6 | IFCAP_TSO6);
24367960e6baSSepherosa Ziehau 	ifp->if_hwassist &= ~(HN_CSUM_IP6_MASK | CSUM_IP6_TSO);
24377960e6baSSepherosa Ziehau 
243815516c77SSepherosa Ziehau 	if (ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) {
24399c6cae24SSepherosa Ziehau 		/*
24409c6cae24SSepherosa Ziehau 		 * Lock hn_set_tso_maxsize() to simplify its
24419c6cae24SSepherosa Ziehau 		 * internal logic.
24429c6cae24SSepherosa Ziehau 		 */
24439c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
244415516c77SSepherosa Ziehau 		hn_set_tso_maxsize(sc, hn_tso_maxlen, ETHERMTU);
24459c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
244615516c77SSepherosa Ziehau 		ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX;
244715516c77SSepherosa Ziehau 		ifp->if_hw_tsomaxsegsize = PAGE_SIZE;
244815516c77SSepherosa Ziehau 	}
244915516c77SSepherosa Ziehau 
245015516c77SSepherosa Ziehau 	ether_ifattach(ifp, eaddr);
245115516c77SSepherosa Ziehau 
245215516c77SSepherosa Ziehau 	if ((ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) && bootverbose) {
245315516c77SSepherosa Ziehau 		if_printf(ifp, "TSO segcnt %u segsz %u\n",
245415516c77SSepherosa Ziehau 		    ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize);
245515516c77SSepherosa Ziehau 	}
2456eb2fe044SSepherosa Ziehau 	if (mtu < ETHERMTU) {
2457eb2fe044SSepherosa Ziehau 		if_printf(ifp, "fixup mtu %u -> %u\n", ifp->if_mtu, mtu);
2458eb2fe044SSepherosa Ziehau 		ifp->if_mtu = mtu;
2459eb2fe044SSepherosa Ziehau 	}
246015516c77SSepherosa Ziehau 
246115516c77SSepherosa Ziehau 	/* Inform the upper layer about the long frame support. */
246215516c77SSepherosa Ziehau 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
246315516c77SSepherosa Ziehau 
246415516c77SSepherosa Ziehau 	/*
246515516c77SSepherosa Ziehau 	 * Kick off link status check.
246615516c77SSepherosa Ziehau 	 */
246715516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
246815516c77SSepherosa Ziehau 	hn_update_link_status(sc);
246915516c77SSepherosa Ziehau 
24709c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
24715bdfd3fdSDexuan Cui 		sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event,
24725bdfd3fdSDexuan Cui 		    hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY);
24735bdfd3fdSDexuan Cui 		sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event,
24745bdfd3fdSDexuan Cui 		    hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY);
24759c6cae24SSepherosa Ziehau 	} else {
24769c6cae24SSepherosa Ziehau 		sc->hn_ifnet_lnkhand = EVENTHANDLER_REGISTER(ifnet_link_event,
24779c6cae24SSepherosa Ziehau 		    hn_ifnet_lnkevent, sc, EVENTHANDLER_PRI_ANY);
24789c6cae24SSepherosa Ziehau 	}
24795bdfd3fdSDexuan Cui 
2480f41e0df4SSepherosa Ziehau 	/*
2481f41e0df4SSepherosa Ziehau 	 * NOTE:
2482f41e0df4SSepherosa Ziehau 	 * Subscribe ether_ifattach event, instead of ifnet_arrival event,
2483f41e0df4SSepherosa Ziehau 	 * since interface's LLADDR is needed; interface LLADDR is not
2484f41e0df4SSepherosa Ziehau 	 * available when ifnet_arrival event is triggered.
2485f41e0df4SSepherosa Ziehau 	 */
2486499c3e17SSepherosa Ziehau 	sc->hn_ifnet_atthand = EVENTHANDLER_REGISTER(ether_ifattach_event,
2487499c3e17SSepherosa Ziehau 	    hn_ifnet_attevent, sc, EVENTHANDLER_PRI_ANY);
2488499c3e17SSepherosa Ziehau 	sc->hn_ifnet_dethand = EVENTHANDLER_REGISTER(ifnet_departure_event,
2489499c3e17SSepherosa Ziehau 	    hn_ifnet_detevent, sc, EVENTHANDLER_PRI_ANY);
2490499c3e17SSepherosa Ziehau 
249115516c77SSepherosa Ziehau 	return (0);
249215516c77SSepherosa Ziehau failed:
249315516c77SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
249415516c77SSepherosa Ziehau 		hn_synth_detach(sc);
249515516c77SSepherosa Ziehau 	hn_detach(dev);
249615516c77SSepherosa Ziehau 	return (error);
249715516c77SSepherosa Ziehau }
249815516c77SSepherosa Ziehau 
249915516c77SSepherosa Ziehau static int
250015516c77SSepherosa Ziehau hn_detach(device_t dev)
250115516c77SSepherosa Ziehau {
250215516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
2503499c3e17SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp, *vf_ifp;
250415516c77SSepherosa Ziehau 
25059c6cae24SSepherosa Ziehau 	if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
25069c6cae24SSepherosa Ziehau 		/*
25079c6cae24SSepherosa Ziehau 		 * In case that the vmbus missed the orphan handler
25089c6cae24SSepherosa Ziehau 		 * installation.
25099c6cae24SSepherosa Ziehau 		 */
25109c6cae24SSepherosa Ziehau 		vmbus_xact_ctx_orphan(sc->hn_xact);
25119c6cae24SSepherosa Ziehau 	}
25129c6cae24SSepherosa Ziehau 
25135bdfd3fdSDexuan Cui 	if (sc->hn_ifaddr_evthand != NULL)
25145bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand);
25155bdfd3fdSDexuan Cui 	if (sc->hn_ifnet_evthand != NULL)
25165bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand);
2517499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_atthand != NULL) {
2518499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ether_ifattach_event,
2519499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_atthand);
2520499c3e17SSepherosa Ziehau 	}
2521499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_dethand != NULL) {
2522499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
2523499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_dethand);
2524499c3e17SSepherosa Ziehau 	}
25259c6cae24SSepherosa Ziehau 	if (sc->hn_ifnet_lnkhand != NULL)
25269c6cae24SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_link_event, sc->hn_ifnet_lnkhand);
2527499c3e17SSepherosa Ziehau 
2528499c3e17SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
2529499c3e17SSepherosa Ziehau 	__compiler_membar();
2530499c3e17SSepherosa Ziehau 	if (vf_ifp != NULL)
2531499c3e17SSepherosa Ziehau 		hn_ifnet_detevent(sc, vf_ifp);
25325bdfd3fdSDexuan Cui 
253315516c77SSepherosa Ziehau 	if (device_is_attached(dev)) {
253415516c77SSepherosa Ziehau 		HN_LOCK(sc);
253515516c77SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
253615516c77SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
25375bdfd3fdSDexuan Cui 				hn_stop(sc, true);
253815516c77SSepherosa Ziehau 			/*
253915516c77SSepherosa Ziehau 			 * NOTE:
254015516c77SSepherosa Ziehau 			 * hn_stop() only suspends data, so managment
254115516c77SSepherosa Ziehau 			 * stuffs have to be suspended manually here.
254215516c77SSepherosa Ziehau 			 */
254315516c77SSepherosa Ziehau 			hn_suspend_mgmt(sc);
254415516c77SSepherosa Ziehau 			hn_synth_detach(sc);
254515516c77SSepherosa Ziehau 		}
254615516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
254715516c77SSepherosa Ziehau 		ether_ifdetach(ifp);
254815516c77SSepherosa Ziehau 	}
254915516c77SSepherosa Ziehau 
255015516c77SSepherosa Ziehau 	ifmedia_removeall(&sc->hn_media);
255115516c77SSepherosa Ziehau 	hn_destroy_rx_data(sc);
255215516c77SSepherosa Ziehau 	hn_destroy_tx_data(sc);
255315516c77SSepherosa Ziehau 
25540e11868dSSepherosa Ziehau 	if (sc->hn_tx_taskqs != NULL && sc->hn_tx_taskqs != hn_tx_taskque) {
2555fdd0222aSSepherosa Ziehau 		int i;
2556fdd0222aSSepherosa Ziehau 
2557fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
2558fdd0222aSSepherosa Ziehau 			taskqueue_free(sc->hn_tx_taskqs[i]);
2559fdd0222aSSepherosa Ziehau 		free(sc->hn_tx_taskqs, M_DEVBUF);
2560fdd0222aSSepherosa Ziehau 	}
256115516c77SSepherosa Ziehau 	taskqueue_free(sc->hn_mgmt_taskq0);
25629c6cae24SSepherosa Ziehau 	if (sc->hn_vf_taskq != NULL)
25639c6cae24SSepherosa Ziehau 		taskqueue_free(sc->hn_vf_taskq);
256415516c77SSepherosa Ziehau 
256525641fc7SSepherosa Ziehau 	if (sc->hn_xact != NULL) {
256625641fc7SSepherosa Ziehau 		/*
256725641fc7SSepherosa Ziehau 		 * Uninstall the orphan handler _before_ the xact is
256825641fc7SSepherosa Ziehau 		 * destructed.
256925641fc7SSepherosa Ziehau 		 */
257025641fc7SSepherosa Ziehau 		vmbus_chan_unset_orphan(sc->hn_prichan);
257115516c77SSepherosa Ziehau 		vmbus_xact_ctx_destroy(sc->hn_xact);
257225641fc7SSepherosa Ziehau 	}
257315516c77SSepherosa Ziehau 
257415516c77SSepherosa Ziehau 	if_free(ifp);
257515516c77SSepherosa Ziehau 
257615516c77SSepherosa Ziehau 	HN_LOCK_DESTROY(sc);
25779c6cae24SSepherosa Ziehau 	rm_destroy(&sc->hn_vf_lock);
257815516c77SSepherosa Ziehau 	return (0);
257915516c77SSepherosa Ziehau }
258015516c77SSepherosa Ziehau 
258115516c77SSepherosa Ziehau static int
258215516c77SSepherosa Ziehau hn_shutdown(device_t dev)
258315516c77SSepherosa Ziehau {
258415516c77SSepherosa Ziehau 
258515516c77SSepherosa Ziehau 	return (0);
258615516c77SSepherosa Ziehau }
258715516c77SSepherosa Ziehau 
258815516c77SSepherosa Ziehau static void
258915516c77SSepherosa Ziehau hn_link_status(struct hn_softc *sc)
259015516c77SSepherosa Ziehau {
259115516c77SSepherosa Ziehau 	uint32_t link_status;
259215516c77SSepherosa Ziehau 	int error;
259315516c77SSepherosa Ziehau 
259415516c77SSepherosa Ziehau 	error = hn_rndis_get_linkstatus(sc, &link_status);
259515516c77SSepherosa Ziehau 	if (error) {
259615516c77SSepherosa Ziehau 		/* XXX what to do? */
259715516c77SSepherosa Ziehau 		return;
259815516c77SSepherosa Ziehau 	}
259915516c77SSepherosa Ziehau 
260015516c77SSepherosa Ziehau 	if (link_status == NDIS_MEDIA_STATE_CONNECTED)
260115516c77SSepherosa Ziehau 		sc->hn_link_flags |= HN_LINK_FLAG_LINKUP;
260215516c77SSepherosa Ziehau 	else
260315516c77SSepherosa Ziehau 		sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
260415516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp,
260515516c77SSepherosa Ziehau 	    (sc->hn_link_flags & HN_LINK_FLAG_LINKUP) ?
260615516c77SSepherosa Ziehau 	    LINK_STATE_UP : LINK_STATE_DOWN);
260715516c77SSepherosa Ziehau }
260815516c77SSepherosa Ziehau 
260915516c77SSepherosa Ziehau static void
261015516c77SSepherosa Ziehau hn_link_taskfunc(void *xsc, int pending __unused)
261115516c77SSepherosa Ziehau {
261215516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
261315516c77SSepherosa Ziehau 
261415516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
261515516c77SSepherosa Ziehau 		return;
261615516c77SSepherosa Ziehau 	hn_link_status(sc);
261715516c77SSepherosa Ziehau }
261815516c77SSepherosa Ziehau 
261915516c77SSepherosa Ziehau static void
262015516c77SSepherosa Ziehau hn_netchg_init_taskfunc(void *xsc, int pending __unused)
262115516c77SSepherosa Ziehau {
262215516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
262315516c77SSepherosa Ziehau 
262415516c77SSepherosa Ziehau 	/* Prevent any link status checks from running. */
262515516c77SSepherosa Ziehau 	sc->hn_link_flags |= HN_LINK_FLAG_NETCHG;
262615516c77SSepherosa Ziehau 
262715516c77SSepherosa Ziehau 	/*
262815516c77SSepherosa Ziehau 	 * Fake up a [link down --> link up] state change; 5 seconds
262915516c77SSepherosa Ziehau 	 * delay is used, which closely simulates miibus reaction
263015516c77SSepherosa Ziehau 	 * upon link down event.
263115516c77SSepherosa Ziehau 	 */
263215516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
263315516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
263415516c77SSepherosa Ziehau 	taskqueue_enqueue_timeout(sc->hn_mgmt_taskq0,
263515516c77SSepherosa Ziehau 	    &sc->hn_netchg_status, 5 * hz);
263615516c77SSepherosa Ziehau }
263715516c77SSepherosa Ziehau 
263815516c77SSepherosa Ziehau static void
263915516c77SSepherosa Ziehau hn_netchg_status_taskfunc(void *xsc, int pending __unused)
264015516c77SSepherosa Ziehau {
264115516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
264215516c77SSepherosa Ziehau 
264315516c77SSepherosa Ziehau 	/* Re-allow link status checks. */
264415516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_NETCHG;
264515516c77SSepherosa Ziehau 	hn_link_status(sc);
264615516c77SSepherosa Ziehau }
264715516c77SSepherosa Ziehau 
264815516c77SSepherosa Ziehau static void
264915516c77SSepherosa Ziehau hn_update_link_status(struct hn_softc *sc)
265015516c77SSepherosa Ziehau {
265115516c77SSepherosa Ziehau 
265215516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
265315516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
265415516c77SSepherosa Ziehau }
265515516c77SSepherosa Ziehau 
265615516c77SSepherosa Ziehau static void
265715516c77SSepherosa Ziehau hn_change_network(struct hn_softc *sc)
265815516c77SSepherosa Ziehau {
265915516c77SSepherosa Ziehau 
266015516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
266115516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_netchg_init);
266215516c77SSepherosa Ziehau }
266315516c77SSepherosa Ziehau 
266415516c77SSepherosa Ziehau static __inline int
266515516c77SSepherosa Ziehau hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
266615516c77SSepherosa Ziehau     struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
266715516c77SSepherosa Ziehau {
266815516c77SSepherosa Ziehau 	struct mbuf *m = *m_head;
266915516c77SSepherosa Ziehau 	int error;
267015516c77SSepherosa Ziehau 
267115516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID, ("txd uses chim"));
267215516c77SSepherosa Ziehau 
267315516c77SSepherosa Ziehau 	error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag, txd->data_dmap,
267415516c77SSepherosa Ziehau 	    m, segs, nsegs, BUS_DMA_NOWAIT);
267515516c77SSepherosa Ziehau 	if (error == EFBIG) {
267615516c77SSepherosa Ziehau 		struct mbuf *m_new;
267715516c77SSepherosa Ziehau 
267815516c77SSepherosa Ziehau 		m_new = m_collapse(m, M_NOWAIT, HN_TX_DATA_SEGCNT_MAX);
267915516c77SSepherosa Ziehau 		if (m_new == NULL)
268015516c77SSepherosa Ziehau 			return ENOBUFS;
268115516c77SSepherosa Ziehau 		else
268215516c77SSepherosa Ziehau 			*m_head = m = m_new;
268315516c77SSepherosa Ziehau 		txr->hn_tx_collapsed++;
268415516c77SSepherosa Ziehau 
268515516c77SSepherosa Ziehau 		error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag,
268615516c77SSepherosa Ziehau 		    txd->data_dmap, m, segs, nsegs, BUS_DMA_NOWAIT);
268715516c77SSepherosa Ziehau 	}
268815516c77SSepherosa Ziehau 	if (!error) {
268915516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag, txd->data_dmap,
269015516c77SSepherosa Ziehau 		    BUS_DMASYNC_PREWRITE);
269115516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_DMAMAP;
269215516c77SSepherosa Ziehau 	}
269315516c77SSepherosa Ziehau 	return error;
269415516c77SSepherosa Ziehau }
269515516c77SSepherosa Ziehau 
269615516c77SSepherosa Ziehau static __inline int
269715516c77SSepherosa Ziehau hn_txdesc_put(struct hn_tx_ring *txr, struct hn_txdesc *txd)
269815516c77SSepherosa Ziehau {
269915516c77SSepherosa Ziehau 
270015516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONLIST) == 0,
270115516c77SSepherosa Ziehau 	    ("put an onlist txd %#x", txd->flags));
2702dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2703dc13fee6SSepherosa Ziehau 	    ("put an onagg txd %#x", txd->flags));
270415516c77SSepherosa Ziehau 
270515516c77SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
270615516c77SSepherosa Ziehau 	if (atomic_fetchadd_int(&txd->refs, -1) != 1)
270715516c77SSepherosa Ziehau 		return 0;
270815516c77SSepherosa Ziehau 
2709dc13fee6SSepherosa Ziehau 	if (!STAILQ_EMPTY(&txd->agg_list)) {
2710dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tmp_txd;
2711dc13fee6SSepherosa Ziehau 
2712dc13fee6SSepherosa Ziehau 		while ((tmp_txd = STAILQ_FIRST(&txd->agg_list)) != NULL) {
2713dc13fee6SSepherosa Ziehau 			int freed;
2714dc13fee6SSepherosa Ziehau 
2715dc13fee6SSepherosa Ziehau 			KASSERT(STAILQ_EMPTY(&tmp_txd->agg_list),
2716dc13fee6SSepherosa Ziehau 			    ("resursive aggregation on aggregated txdesc"));
2717dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_ONAGG),
2718dc13fee6SSepherosa Ziehau 			    ("not aggregated txdesc"));
2719dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
2720dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc uses dmamap"));
2721dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
2722dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc consumes "
2723dc13fee6SSepherosa Ziehau 			     "chimney sending buffer"));
2724dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_size == 0,
2725dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc has non-zero "
2726dc13fee6SSepherosa Ziehau 			     "chimney sending size"));
2727dc13fee6SSepherosa Ziehau 
2728dc13fee6SSepherosa Ziehau 			STAILQ_REMOVE_HEAD(&txd->agg_list, agg_link);
2729dc13fee6SSepherosa Ziehau 			tmp_txd->flags &= ~HN_TXD_FLAG_ONAGG;
2730dc13fee6SSepherosa Ziehau 			freed = hn_txdesc_put(txr, tmp_txd);
2731dc13fee6SSepherosa Ziehau 			KASSERT(freed, ("failed to free aggregated txdesc"));
2732dc13fee6SSepherosa Ziehau 		}
2733dc13fee6SSepherosa Ziehau 	}
2734dc13fee6SSepherosa Ziehau 
273515516c77SSepherosa Ziehau 	if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) {
273615516c77SSepherosa Ziehau 		KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
273715516c77SSepherosa Ziehau 		    ("chim txd uses dmamap"));
273815516c77SSepherosa Ziehau 		hn_chim_free(txr->hn_sc, txd->chim_index);
273915516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
2740dc13fee6SSepherosa Ziehau 		txd->chim_size = 0;
274115516c77SSepherosa Ziehau 	} else if (txd->flags & HN_TXD_FLAG_DMAMAP) {
274215516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag,
274315516c77SSepherosa Ziehau 		    txd->data_dmap, BUS_DMASYNC_POSTWRITE);
274415516c77SSepherosa Ziehau 		bus_dmamap_unload(txr->hn_tx_data_dtag,
274515516c77SSepherosa Ziehau 		    txd->data_dmap);
274615516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_DMAMAP;
274715516c77SSepherosa Ziehau 	}
274815516c77SSepherosa Ziehau 
274915516c77SSepherosa Ziehau 	if (txd->m != NULL) {
275015516c77SSepherosa Ziehau 		m_freem(txd->m);
275115516c77SSepherosa Ziehau 		txd->m = NULL;
275215516c77SSepherosa Ziehau 	}
275315516c77SSepherosa Ziehau 
275415516c77SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONLIST;
275515516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
275615516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
275715516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_avail >= 0 &&
275815516c77SSepherosa Ziehau 	    txr->hn_txdesc_avail < txr->hn_txdesc_cnt,
275915516c77SSepherosa Ziehau 	    ("txdesc_put: invalid txd avail %d", txr->hn_txdesc_avail));
276015516c77SSepherosa Ziehau 	txr->hn_txdesc_avail++;
276115516c77SSepherosa Ziehau 	SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
276215516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
276385e4ae1eSSepherosa Ziehau #else	/* HN_USE_TXDESC_BUFRING */
276485e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
276515516c77SSepherosa Ziehau 	atomic_add_int(&txr->hn_txdesc_avail, 1);
276615516c77SSepherosa Ziehau #endif
276785e4ae1eSSepherosa Ziehau 	buf_ring_enqueue(txr->hn_txdesc_br, txd);
276885e4ae1eSSepherosa Ziehau #endif	/* !HN_USE_TXDESC_BUFRING */
276915516c77SSepherosa Ziehau 
277015516c77SSepherosa Ziehau 	return 1;
277115516c77SSepherosa Ziehau }
277215516c77SSepherosa Ziehau 
277315516c77SSepherosa Ziehau static __inline struct hn_txdesc *
277415516c77SSepherosa Ziehau hn_txdesc_get(struct hn_tx_ring *txr)
277515516c77SSepherosa Ziehau {
277615516c77SSepherosa Ziehau 	struct hn_txdesc *txd;
277715516c77SSepherosa Ziehau 
277815516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
277915516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
278015516c77SSepherosa Ziehau 	txd = SLIST_FIRST(&txr->hn_txlist);
278115516c77SSepherosa Ziehau 	if (txd != NULL) {
278215516c77SSepherosa Ziehau 		KASSERT(txr->hn_txdesc_avail > 0,
278315516c77SSepherosa Ziehau 		    ("txdesc_get: invalid txd avail %d", txr->hn_txdesc_avail));
278415516c77SSepherosa Ziehau 		txr->hn_txdesc_avail--;
278515516c77SSepherosa Ziehau 		SLIST_REMOVE_HEAD(&txr->hn_txlist, link);
278615516c77SSepherosa Ziehau 	}
278715516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
278815516c77SSepherosa Ziehau #else
278915516c77SSepherosa Ziehau 	txd = buf_ring_dequeue_sc(txr->hn_txdesc_br);
279015516c77SSepherosa Ziehau #endif
279115516c77SSepherosa Ziehau 
279215516c77SSepherosa Ziehau 	if (txd != NULL) {
279315516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
279485e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
279515516c77SSepherosa Ziehau 		atomic_subtract_int(&txr->hn_txdesc_avail, 1);
279615516c77SSepherosa Ziehau #endif
279785e4ae1eSSepherosa Ziehau #endif	/* HN_USE_TXDESC_BUFRING */
279815516c77SSepherosa Ziehau 		KASSERT(txd->m == NULL && txd->refs == 0 &&
2799dc13fee6SSepherosa Ziehau 		    STAILQ_EMPTY(&txd->agg_list) &&
280015516c77SSepherosa Ziehau 		    txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
2801dc13fee6SSepherosa Ziehau 		    txd->chim_size == 0 &&
280215516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONLIST) &&
2803dc13fee6SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONAGG) == 0 &&
280415516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("invalid txd"));
280515516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_ONLIST;
280615516c77SSepherosa Ziehau 		txd->refs = 1;
280715516c77SSepherosa Ziehau 	}
280815516c77SSepherosa Ziehau 	return txd;
280915516c77SSepherosa Ziehau }
281015516c77SSepherosa Ziehau 
281115516c77SSepherosa Ziehau static __inline void
281215516c77SSepherosa Ziehau hn_txdesc_hold(struct hn_txdesc *txd)
281315516c77SSepherosa Ziehau {
281415516c77SSepherosa Ziehau 
281515516c77SSepherosa Ziehau 	/* 0->1 transition will never work */
281625641fc7SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
281715516c77SSepherosa Ziehau 	atomic_add_int(&txd->refs, 1);
281815516c77SSepherosa Ziehau }
281915516c77SSepherosa Ziehau 
2820dc13fee6SSepherosa Ziehau static __inline void
2821dc13fee6SSepherosa Ziehau hn_txdesc_agg(struct hn_txdesc *agg_txd, struct hn_txdesc *txd)
2822dc13fee6SSepherosa Ziehau {
2823dc13fee6SSepherosa Ziehau 
2824dc13fee6SSepherosa Ziehau 	KASSERT((agg_txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2825dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on aggregating txdesc"));
2826dc13fee6SSepherosa Ziehau 
2827dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2828dc13fee6SSepherosa Ziehau 	    ("already aggregated"));
2829dc13fee6SSepherosa Ziehau 	KASSERT(STAILQ_EMPTY(&txd->agg_list),
2830dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on to-be-aggregated txdesc"));
2831dc13fee6SSepherosa Ziehau 
2832dc13fee6SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONAGG;
2833dc13fee6SSepherosa Ziehau 	STAILQ_INSERT_TAIL(&agg_txd->agg_list, txd, agg_link);
2834dc13fee6SSepherosa Ziehau }
2835dc13fee6SSepherosa Ziehau 
283615516c77SSepherosa Ziehau static bool
283715516c77SSepherosa Ziehau hn_tx_ring_pending(struct hn_tx_ring *txr)
283815516c77SSepherosa Ziehau {
283915516c77SSepherosa Ziehau 	bool pending = false;
284015516c77SSepherosa Ziehau 
284115516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
284215516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
284315516c77SSepherosa Ziehau 	if (txr->hn_txdesc_avail != txr->hn_txdesc_cnt)
284415516c77SSepherosa Ziehau 		pending = true;
284515516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
284615516c77SSepherosa Ziehau #else
284715516c77SSepherosa Ziehau 	if (!buf_ring_full(txr->hn_txdesc_br))
284815516c77SSepherosa Ziehau 		pending = true;
284915516c77SSepherosa Ziehau #endif
285015516c77SSepherosa Ziehau 	return (pending);
285115516c77SSepherosa Ziehau }
285215516c77SSepherosa Ziehau 
285315516c77SSepherosa Ziehau static __inline void
285415516c77SSepherosa Ziehau hn_txeof(struct hn_tx_ring *txr)
285515516c77SSepherosa Ziehau {
285615516c77SSepherosa Ziehau 	txr->hn_has_txeof = 0;
285715516c77SSepherosa Ziehau 	txr->hn_txeof(txr);
285815516c77SSepherosa Ziehau }
285915516c77SSepherosa Ziehau 
286015516c77SSepherosa Ziehau static void
286115516c77SSepherosa Ziehau hn_txpkt_done(struct hn_nvs_sendctx *sndc, struct hn_softc *sc,
286215516c77SSepherosa Ziehau     struct vmbus_channel *chan, const void *data __unused, int dlen __unused)
286315516c77SSepherosa Ziehau {
286415516c77SSepherosa Ziehau 	struct hn_txdesc *txd = sndc->hn_cbarg;
286515516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
286615516c77SSepherosa Ziehau 
286715516c77SSepherosa Ziehau 	txr = txd->txr;
286815516c77SSepherosa Ziehau 	KASSERT(txr->hn_chan == chan,
286915516c77SSepherosa Ziehau 	    ("channel mismatch, on chan%u, should be chan%u",
2870aa1a2adcSSepherosa Ziehau 	     vmbus_chan_id(chan), vmbus_chan_id(txr->hn_chan)));
287115516c77SSepherosa Ziehau 
287215516c77SSepherosa Ziehau 	txr->hn_has_txeof = 1;
287315516c77SSepherosa Ziehau 	hn_txdesc_put(txr, txd);
287415516c77SSepherosa Ziehau 
287515516c77SSepherosa Ziehau 	++txr->hn_txdone_cnt;
287615516c77SSepherosa Ziehau 	if (txr->hn_txdone_cnt >= HN_EARLY_TXEOF_THRESH) {
287715516c77SSepherosa Ziehau 		txr->hn_txdone_cnt = 0;
287815516c77SSepherosa Ziehau 		if (txr->hn_oactive)
287915516c77SSepherosa Ziehau 			hn_txeof(txr);
288015516c77SSepherosa Ziehau 	}
288115516c77SSepherosa Ziehau }
288215516c77SSepherosa Ziehau 
288315516c77SSepherosa Ziehau static void
288415516c77SSepherosa Ziehau hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
288515516c77SSepherosa Ziehau {
288615516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
288726d79d40SMichael Tuexen 	struct epoch_tracker et;
288826d79d40SMichael Tuexen 
288926d79d40SMichael Tuexen 	NET_EPOCH_ENTER(et);
289015516c77SSepherosa Ziehau 	tcp_lro_flush_all(&rxr->hn_lro);
289126d79d40SMichael Tuexen 	NET_EPOCH_EXIT(et);
289215516c77SSepherosa Ziehau #endif
289315516c77SSepherosa Ziehau 
289415516c77SSepherosa Ziehau 	/*
289515516c77SSepherosa Ziehau 	 * NOTE:
289615516c77SSepherosa Ziehau 	 * 'txr' could be NULL, if multiple channels and
289715516c77SSepherosa Ziehau 	 * ifnet.if_start method are enabled.
289815516c77SSepherosa Ziehau 	 */
289915516c77SSepherosa Ziehau 	if (txr == NULL || !txr->hn_has_txeof)
290015516c77SSepherosa Ziehau 		return;
290115516c77SSepherosa Ziehau 
290215516c77SSepherosa Ziehau 	txr->hn_txdone_cnt = 0;
290315516c77SSepherosa Ziehau 	hn_txeof(txr);
290415516c77SSepherosa Ziehau }
290515516c77SSepherosa Ziehau 
290615516c77SSepherosa Ziehau static __inline uint32_t
290715516c77SSepherosa Ziehau hn_rndis_pktmsg_offset(uint32_t ofs)
290815516c77SSepherosa Ziehau {
290915516c77SSepherosa Ziehau 
291015516c77SSepherosa Ziehau 	KASSERT(ofs >= sizeof(struct rndis_packet_msg),
291115516c77SSepherosa Ziehau 	    ("invalid RNDIS packet msg offset %u", ofs));
291215516c77SSepherosa Ziehau 	return (ofs - __offsetof(struct rndis_packet_msg, rm_dataoffset));
291315516c77SSepherosa Ziehau }
291415516c77SSepherosa Ziehau 
291515516c77SSepherosa Ziehau static __inline void *
291615516c77SSepherosa Ziehau hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
291715516c77SSepherosa Ziehau     size_t pi_dlen, uint32_t pi_type)
291815516c77SSepherosa Ziehau {
291915516c77SSepherosa Ziehau 	const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
292015516c77SSepherosa Ziehau 	struct rndis_pktinfo *pi;
292115516c77SSepherosa Ziehau 
292215516c77SSepherosa Ziehau 	KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
292315516c77SSepherosa Ziehau 	    ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
292415516c77SSepherosa Ziehau 
292515516c77SSepherosa Ziehau 	/*
292615516c77SSepherosa Ziehau 	 * Per-packet-info does not move; it only grows.
292715516c77SSepherosa Ziehau 	 *
292815516c77SSepherosa Ziehau 	 * NOTE:
292915516c77SSepherosa Ziehau 	 * rm_pktinfooffset in this phase counts from the beginning
293015516c77SSepherosa Ziehau 	 * of rndis_packet_msg.
293115516c77SSepherosa Ziehau 	 */
293215516c77SSepherosa Ziehau 	KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
293315516c77SSepherosa Ziehau 	    ("%u pktinfo overflows RNDIS packet msg", pi_type));
293415516c77SSepherosa Ziehau 	pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
293515516c77SSepherosa Ziehau 	    pkt->rm_pktinfolen);
293615516c77SSepherosa Ziehau 	pkt->rm_pktinfolen += pi_size;
293715516c77SSepherosa Ziehau 
293815516c77SSepherosa Ziehau 	pi->rm_size = pi_size;
293915516c77SSepherosa Ziehau 	pi->rm_type = pi_type;
2940805dbff6SWei Hu 	pi->rm_internal = 0;
294115516c77SSepherosa Ziehau 	pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
294215516c77SSepherosa Ziehau 
294315516c77SSepherosa Ziehau 	return (pi->rm_data);
294415516c77SSepherosa Ziehau }
294515516c77SSepherosa Ziehau 
2946dc13fee6SSepherosa Ziehau static __inline int
2947dc13fee6SSepherosa Ziehau hn_flush_txagg(struct ifnet *ifp, struct hn_tx_ring *txr)
2948dc13fee6SSepherosa Ziehau {
2949dc13fee6SSepherosa Ziehau 	struct hn_txdesc *txd;
2950dc13fee6SSepherosa Ziehau 	struct mbuf *m;
2951dc13fee6SSepherosa Ziehau 	int error, pkts;
2952dc13fee6SSepherosa Ziehau 
2953dc13fee6SSepherosa Ziehau 	txd = txr->hn_agg_txd;
2954dc13fee6SSepherosa Ziehau 	KASSERT(txd != NULL, ("no aggregate txdesc"));
2955dc13fee6SSepherosa Ziehau 
2956dc13fee6SSepherosa Ziehau 	/*
2957dc13fee6SSepherosa Ziehau 	 * Since hn_txpkt() will reset this temporary stat, save
2958dc13fee6SSepherosa Ziehau 	 * it now, so that oerrors can be updated properly, if
2959dc13fee6SSepherosa Ziehau 	 * hn_txpkt() ever fails.
2960dc13fee6SSepherosa Ziehau 	 */
2961dc13fee6SSepherosa Ziehau 	pkts = txr->hn_stat_pkts;
2962dc13fee6SSepherosa Ziehau 
2963dc13fee6SSepherosa Ziehau 	/*
2964dc13fee6SSepherosa Ziehau 	 * Since txd's mbuf will _not_ be freed upon hn_txpkt()
2965dc13fee6SSepherosa Ziehau 	 * failure, save it for later freeing, if hn_txpkt() ever
2966dc13fee6SSepherosa Ziehau 	 * fails.
2967dc13fee6SSepherosa Ziehau 	 */
2968dc13fee6SSepherosa Ziehau 	m = txd->m;
2969dc13fee6SSepherosa Ziehau 	error = hn_txpkt(ifp, txr, txd);
2970dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
2971dc13fee6SSepherosa Ziehau 		/* txd is freed, but m is not. */
2972dc13fee6SSepherosa Ziehau 		m_freem(m);
2973dc13fee6SSepherosa Ziehau 
2974dc13fee6SSepherosa Ziehau 		txr->hn_flush_failed++;
2975dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, pkts);
2976dc13fee6SSepherosa Ziehau 	}
2977dc13fee6SSepherosa Ziehau 
2978dc13fee6SSepherosa Ziehau 	/* Reset all aggregation states. */
2979dc13fee6SSepherosa Ziehau 	txr->hn_agg_txd = NULL;
2980dc13fee6SSepherosa Ziehau 	txr->hn_agg_szleft = 0;
2981dc13fee6SSepherosa Ziehau 	txr->hn_agg_pktleft = 0;
2982dc13fee6SSepherosa Ziehau 	txr->hn_agg_prevpkt = NULL;
2983dc13fee6SSepherosa Ziehau 
2984dc13fee6SSepherosa Ziehau 	return (error);
2985dc13fee6SSepherosa Ziehau }
2986dc13fee6SSepherosa Ziehau 
2987dc13fee6SSepherosa Ziehau static void *
2988dc13fee6SSepherosa Ziehau hn_try_txagg(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
2989dc13fee6SSepherosa Ziehau     int pktsize)
2990dc13fee6SSepherosa Ziehau {
2991dc13fee6SSepherosa Ziehau 	void *chim;
2992dc13fee6SSepherosa Ziehau 
2993dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL) {
2994dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft >= 1 && txr->hn_agg_szleft > pktsize) {
2995dc13fee6SSepherosa Ziehau 			struct hn_txdesc *agg_txd = txr->hn_agg_txd;
2996dc13fee6SSepherosa Ziehau 			struct rndis_packet_msg *pkt = txr->hn_agg_prevpkt;
2997dc13fee6SSepherosa Ziehau 			int olen;
2998dc13fee6SSepherosa Ziehau 
2999dc13fee6SSepherosa Ziehau 			/*
3000dc13fee6SSepherosa Ziehau 			 * Update the previous RNDIS packet's total length,
3001dc13fee6SSepherosa Ziehau 			 * it can be increased due to the mandatory alignment
3002dc13fee6SSepherosa Ziehau 			 * padding for this RNDIS packet.  And update the
3003dc13fee6SSepherosa Ziehau 			 * aggregating txdesc's chimney sending buffer size
3004dc13fee6SSepherosa Ziehau 			 * accordingly.
3005dc13fee6SSepherosa Ziehau 			 *
3006dc13fee6SSepherosa Ziehau 			 * XXX
3007dc13fee6SSepherosa Ziehau 			 * Zero-out the padding, as required by the RNDIS spec.
3008dc13fee6SSepherosa Ziehau 			 */
3009dc13fee6SSepherosa Ziehau 			olen = pkt->rm_len;
3010dc13fee6SSepherosa Ziehau 			pkt->rm_len = roundup2(olen, txr->hn_agg_align);
3011dc13fee6SSepherosa Ziehau 			agg_txd->chim_size += pkt->rm_len - olen;
3012dc13fee6SSepherosa Ziehau 
3013dc13fee6SSepherosa Ziehau 			/* Link this txdesc to the parent. */
3014dc13fee6SSepherosa Ziehau 			hn_txdesc_agg(agg_txd, txd);
3015dc13fee6SSepherosa Ziehau 
3016dc13fee6SSepherosa Ziehau 			chim = (uint8_t *)pkt + pkt->rm_len;
3017dc13fee6SSepherosa Ziehau 			/* Save the current packet for later fixup. */
3018dc13fee6SSepherosa Ziehau 			txr->hn_agg_prevpkt = chim;
3019dc13fee6SSepherosa Ziehau 
3020dc13fee6SSepherosa Ziehau 			txr->hn_agg_pktleft--;
3021dc13fee6SSepherosa Ziehau 			txr->hn_agg_szleft -= pktsize;
3022dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_szleft <=
3023dc13fee6SSepherosa Ziehau 			    HN_PKTSIZE_MIN(txr->hn_agg_align)) {
3024dc13fee6SSepherosa Ziehau 				/*
3025dc13fee6SSepherosa Ziehau 				 * Probably can't aggregate more packets,
3026dc13fee6SSepherosa Ziehau 				 * flush this aggregating txdesc proactively.
3027dc13fee6SSepherosa Ziehau 				 */
3028dc13fee6SSepherosa Ziehau 				txr->hn_agg_pktleft = 0;
3029dc13fee6SSepherosa Ziehau 			}
3030dc13fee6SSepherosa Ziehau 			/* Done! */
3031dc13fee6SSepherosa Ziehau 			return (chim);
3032dc13fee6SSepherosa Ziehau 		}
3033dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
3034dc13fee6SSepherosa Ziehau 	}
3035dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
3036dc13fee6SSepherosa Ziehau 
3037dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney_tried++;
3038dc13fee6SSepherosa Ziehau 	txd->chim_index = hn_chim_alloc(txr->hn_sc);
3039dc13fee6SSepherosa Ziehau 	if (txd->chim_index == HN_NVS_CHIM_IDX_INVALID)
3040dc13fee6SSepherosa Ziehau 		return (NULL);
3041dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney++;
3042dc13fee6SSepherosa Ziehau 
3043dc13fee6SSepherosa Ziehau 	chim = txr->hn_sc->hn_chim +
3044dc13fee6SSepherosa Ziehau 	    (txd->chim_index * txr->hn_sc->hn_chim_szmax);
3045dc13fee6SSepherosa Ziehau 
3046dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_pktmax > 1 &&
3047dc13fee6SSepherosa Ziehau 	    txr->hn_agg_szmax > pktsize + HN_PKTSIZE_MIN(txr->hn_agg_align)) {
3048dc13fee6SSepherosa Ziehau 		txr->hn_agg_txd = txd;
3049dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktleft = txr->hn_agg_pktmax - 1;
3050dc13fee6SSepherosa Ziehau 		txr->hn_agg_szleft = txr->hn_agg_szmax - pktsize;
3051dc13fee6SSepherosa Ziehau 		txr->hn_agg_prevpkt = chim;
3052dc13fee6SSepherosa Ziehau 	}
3053dc13fee6SSepherosa Ziehau 	return (chim);
3054dc13fee6SSepherosa Ziehau }
3055dc13fee6SSepherosa Ziehau 
305615516c77SSepherosa Ziehau /*
305715516c77SSepherosa Ziehau  * NOTE:
305815516c77SSepherosa Ziehau  * If this function fails, then both txd and m_head0 will be freed.
305915516c77SSepherosa Ziehau  */
306015516c77SSepherosa Ziehau static int
3061dc13fee6SSepherosa Ziehau hn_encap(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
3062dc13fee6SSepherosa Ziehau     struct mbuf **m_head0)
306315516c77SSepherosa Ziehau {
306415516c77SSepherosa Ziehau 	bus_dma_segment_t segs[HN_TX_DATA_SEGCNT_MAX];
306515516c77SSepherosa Ziehau 	int error, nsegs, i;
306615516c77SSepherosa Ziehau 	struct mbuf *m_head = *m_head0;
306715516c77SSepherosa Ziehau 	struct rndis_packet_msg *pkt;
306815516c77SSepherosa Ziehau 	uint32_t *pi_data;
30698966e5d5SSepherosa Ziehau 	void *chim = NULL;
3070dc13fee6SSepherosa Ziehau 	int pkt_hlen, pkt_size;
307115516c77SSepherosa Ziehau 
307215516c77SSepherosa Ziehau 	pkt = txd->rndis_pkt;
3073dc13fee6SSepherosa Ziehau 	pkt_size = HN_PKTSIZE(m_head, txr->hn_agg_align);
3074dc13fee6SSepherosa Ziehau 	if (pkt_size < txr->hn_chim_size) {
3075dc13fee6SSepherosa Ziehau 		chim = hn_try_txagg(ifp, txr, txd, pkt_size);
3076dc13fee6SSepherosa Ziehau 		if (chim != NULL)
30778966e5d5SSepherosa Ziehau 			pkt = chim;
3078dc13fee6SSepherosa Ziehau 	} else {
3079dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL)
3080dc13fee6SSepherosa Ziehau 			hn_flush_txagg(ifp, txr);
30818966e5d5SSepherosa Ziehau 	}
30828966e5d5SSepherosa Ziehau 
308315516c77SSepherosa Ziehau 	pkt->rm_type = REMOTE_NDIS_PACKET_MSG;
30848fe90f73SSepherosa Ziehau 	pkt->rm_len = m_head->m_pkthdr.len;
30859130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = 0;
308615516c77SSepherosa Ziehau 	pkt->rm_datalen = m_head->m_pkthdr.len;
3087dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataoffset = 0;
3088dc13fee6SSepherosa Ziehau 	pkt->rm_oobdatalen = 0;
3089dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataelements = 0;
309015516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = sizeof(*pkt);
309115516c77SSepherosa Ziehau 	pkt->rm_pktinfolen = 0;
3092dc13fee6SSepherosa Ziehau 	pkt->rm_vchandle = 0;
3093dc13fee6SSepherosa Ziehau 	pkt->rm_reserved = 0;
309415516c77SSepherosa Ziehau 
309515516c77SSepherosa Ziehau 	if (txr->hn_tx_flags & HN_TX_FLAG_HASHVAL) {
309615516c77SSepherosa Ziehau 		/*
309780f39bd9SWei Hu 		 * Set the hash value for this packet.
309815516c77SSepherosa Ziehau 		 */
309915516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
310015516c77SSepherosa Ziehau 		    HN_NDIS_HASH_VALUE_SIZE, HN_NDIS_PKTINFO_TYPE_HASHVAL);
310180f39bd9SWei Hu 
310280f39bd9SWei Hu 		if (M_HASHTYPE_ISHASH(m_head))
310380f39bd9SWei Hu 			/*
310480f39bd9SWei Hu 			 * The flowid field contains the hash value host
310580f39bd9SWei Hu 			 * set in the rx queue if it is a ip forwarding pkt.
310680f39bd9SWei Hu 			 * Set the same hash value so host can send on the
310780f39bd9SWei Hu 			 * cpu it was received.
310880f39bd9SWei Hu 			 */
310980f39bd9SWei Hu 			*pi_data = m_head->m_pkthdr.flowid;
311080f39bd9SWei Hu 		else
311180f39bd9SWei Hu 			/*
311280f39bd9SWei Hu 			 * Otherwise just put the tx queue index.
311380f39bd9SWei Hu 			 */
311415516c77SSepherosa Ziehau 			*pi_data = txr->hn_tx_idx;
311515516c77SSepherosa Ziehau 	}
311615516c77SSepherosa Ziehau 
311715516c77SSepherosa Ziehau 	if (m_head->m_flags & M_VLANTAG) {
311815516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
311915516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_SIZE, NDIS_PKTINFO_TYPE_VLAN);
312015516c77SSepherosa Ziehau 		*pi_data = NDIS_VLAN_INFO_MAKE(
312115516c77SSepherosa Ziehau 		    EVL_VLANOFTAG(m_head->m_pkthdr.ether_vtag),
312215516c77SSepherosa Ziehau 		    EVL_PRIOFTAG(m_head->m_pkthdr.ether_vtag),
312315516c77SSepherosa Ziehau 		    EVL_CFIOFTAG(m_head->m_pkthdr.ether_vtag));
312415516c77SSepherosa Ziehau 	}
312515516c77SSepherosa Ziehau 
312615516c77SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
312715516c77SSepherosa Ziehau #if defined(INET6) || defined(INET)
312815516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
312915516c77SSepherosa Ziehau 		    NDIS_LSO2_INFO_SIZE, NDIS_PKTINFO_TYPE_LSO);
313015516c77SSepherosa Ziehau #ifdef INET
313115516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
3132c49d47daSSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV4(
3133c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen,
313415516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
313515516c77SSepherosa Ziehau 		}
313615516c77SSepherosa Ziehau #endif
313715516c77SSepherosa Ziehau #if defined(INET6) && defined(INET)
313815516c77SSepherosa Ziehau 		else
313915516c77SSepherosa Ziehau #endif
314015516c77SSepherosa Ziehau #ifdef INET6
314115516c77SSepherosa Ziehau 		{
3142c49d47daSSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV6(
3143c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen,
314415516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
314515516c77SSepherosa Ziehau 		}
314615516c77SSepherosa Ziehau #endif
314715516c77SSepherosa Ziehau #endif	/* INET6 || INET */
314815516c77SSepherosa Ziehau 	} else if (m_head->m_pkthdr.csum_flags & txr->hn_csum_assist) {
314915516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
315015516c77SSepherosa Ziehau 		    NDIS_TXCSUM_INFO_SIZE, NDIS_PKTINFO_TYPE_CSUM);
315115516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
315215516c77SSepherosa Ziehau 		    (CSUM_IP6_TCP | CSUM_IP6_UDP)) {
315315516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV6;
315415516c77SSepherosa Ziehau 		} else {
315515516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV4;
315615516c77SSepherosa Ziehau 			if (m_head->m_pkthdr.csum_flags & CSUM_IP)
315715516c77SSepherosa Ziehau 				*pi_data |= NDIS_TXCSUM_INFO_IPCS;
315815516c77SSepherosa Ziehau 		}
315915516c77SSepherosa Ziehau 
3160c49d47daSSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
3161c49d47daSSepherosa Ziehau 		    (CSUM_IP_TCP | CSUM_IP6_TCP)) {
3162c49d47daSSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_MKTCPCS(
3163c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen);
3164c49d47daSSepherosa Ziehau 		} else if (m_head->m_pkthdr.csum_flags &
3165c49d47daSSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP6_UDP)) {
3166c49d47daSSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_MKUDPCS(
3167c49d47daSSepherosa Ziehau 			    m_head->m_pkthdr.l2hlen + m_head->m_pkthdr.l3hlen);
3168c49d47daSSepherosa Ziehau 		}
316915516c77SSepherosa Ziehau 	}
317015516c77SSepherosa Ziehau 
3171dc13fee6SSepherosa Ziehau 	pkt_hlen = pkt->rm_pktinfooffset + pkt->rm_pktinfolen;
31728fe90f73SSepherosa Ziehau 	/* Fixup RNDIS packet message total length */
31738fe90f73SSepherosa Ziehau 	pkt->rm_len += pkt_hlen;
317415516c77SSepherosa Ziehau 	/* Convert RNDIS packet message offsets */
31759130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = hn_rndis_pktmsg_offset(pkt_hlen);
317615516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = hn_rndis_pktmsg_offset(pkt->rm_pktinfooffset);
317715516c77SSepherosa Ziehau 
317815516c77SSepherosa Ziehau 	/*
31798966e5d5SSepherosa Ziehau 	 * Fast path: Chimney sending.
318015516c77SSepherosa Ziehau 	 */
31818966e5d5SSepherosa Ziehau 	if (chim != NULL) {
3182dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tgt_txd = txd;
3183dc13fee6SSepherosa Ziehau 
3184dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL) {
3185dc13fee6SSepherosa Ziehau 			tgt_txd = txr->hn_agg_txd;
3186dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
3187dc13fee6SSepherosa Ziehau 			*m_head0 = NULL;
3188dc13fee6SSepherosa Ziehau #endif
3189dc13fee6SSepherosa Ziehau 		}
3190dc13fee6SSepherosa Ziehau 
3191dc13fee6SSepherosa Ziehau 		KASSERT(pkt == chim,
3192dc13fee6SSepherosa Ziehau 		    ("RNDIS pkt not in chimney sending buffer"));
3193dc13fee6SSepherosa Ziehau 		KASSERT(tgt_txd->chim_index != HN_NVS_CHIM_IDX_INVALID,
3194dc13fee6SSepherosa Ziehau 		    ("chimney sending buffer is not used"));
3195dc13fee6SSepherosa Ziehau 		tgt_txd->chim_size += pkt->rm_len;
319615516c77SSepherosa Ziehau 
31978966e5d5SSepherosa Ziehau 		m_copydata(m_head, 0, m_head->m_pkthdr.len,
3198dc13fee6SSepherosa Ziehau 		    ((uint8_t *)chim) + pkt_hlen);
319915516c77SSepherosa Ziehau 
320015516c77SSepherosa Ziehau 		txr->hn_gpa_cnt = 0;
320115516c77SSepherosa Ziehau 		txr->hn_sendpkt = hn_txpkt_chim;
320215516c77SSepherosa Ziehau 		goto done;
320315516c77SSepherosa Ziehau 	}
3204dc13fee6SSepherosa Ziehau 
3205dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("aggregating sglist txdesc"));
32068966e5d5SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
32078966e5d5SSepherosa Ziehau 	    ("chimney buffer is used"));
32088966e5d5SSepherosa Ziehau 	KASSERT(pkt == txd->rndis_pkt, ("RNDIS pkt not in txdesc"));
320915516c77SSepherosa Ziehau 
321015516c77SSepherosa Ziehau 	error = hn_txdesc_dmamap_load(txr, txd, &m_head, segs, &nsegs);
3211dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
321215516c77SSepherosa Ziehau 		int freed;
321315516c77SSepherosa Ziehau 
321415516c77SSepherosa Ziehau 		/*
321515516c77SSepherosa Ziehau 		 * This mbuf is not linked w/ the txd yet, so free it now.
321615516c77SSepherosa Ziehau 		 */
321715516c77SSepherosa Ziehau 		m_freem(m_head);
321815516c77SSepherosa Ziehau 		*m_head0 = NULL;
321915516c77SSepherosa Ziehau 
322015516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
322115516c77SSepherosa Ziehau 		KASSERT(freed != 0,
322215516c77SSepherosa Ziehau 		    ("fail to free txd upon txdma error"));
322315516c77SSepherosa Ziehau 
322415516c77SSepherosa Ziehau 		txr->hn_txdma_failed++;
3225dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
322615516c77SSepherosa Ziehau 		return error;
322715516c77SSepherosa Ziehau 	}
322815516c77SSepherosa Ziehau 	*m_head0 = m_head;
322915516c77SSepherosa Ziehau 
323015516c77SSepherosa Ziehau 	/* +1 RNDIS packet message */
323115516c77SSepherosa Ziehau 	txr->hn_gpa_cnt = nsegs + 1;
323215516c77SSepherosa Ziehau 
323315516c77SSepherosa Ziehau 	/* send packet with page buffer */
323415516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_page = atop(txd->rndis_pkt_paddr);
323515516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_ofs = txd->rndis_pkt_paddr & PAGE_MASK;
3236dc13fee6SSepherosa Ziehau 	txr->hn_gpa[0].gpa_len = pkt_hlen;
323715516c77SSepherosa Ziehau 
323815516c77SSepherosa Ziehau 	/*
323915516c77SSepherosa Ziehau 	 * Fill the page buffers with mbuf info after the page
324015516c77SSepherosa Ziehau 	 * buffer for RNDIS packet message.
324115516c77SSepherosa Ziehau 	 */
324215516c77SSepherosa Ziehau 	for (i = 0; i < nsegs; ++i) {
324315516c77SSepherosa Ziehau 		struct vmbus_gpa *gpa = &txr->hn_gpa[i + 1];
324415516c77SSepherosa Ziehau 
324515516c77SSepherosa Ziehau 		gpa->gpa_page = atop(segs[i].ds_addr);
324615516c77SSepherosa Ziehau 		gpa->gpa_ofs = segs[i].ds_addr & PAGE_MASK;
324715516c77SSepherosa Ziehau 		gpa->gpa_len = segs[i].ds_len;
324815516c77SSepherosa Ziehau 	}
324915516c77SSepherosa Ziehau 
325015516c77SSepherosa Ziehau 	txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
325115516c77SSepherosa Ziehau 	txd->chim_size = 0;
325215516c77SSepherosa Ziehau 	txr->hn_sendpkt = hn_txpkt_sglist;
325315516c77SSepherosa Ziehau done:
325415516c77SSepherosa Ziehau 	txd->m = m_head;
325515516c77SSepherosa Ziehau 
325615516c77SSepherosa Ziehau 	/* Set the completion routine */
325715516c77SSepherosa Ziehau 	hn_nvs_sendctx_init(&txd->send_ctx, hn_txpkt_done, txd);
325815516c77SSepherosa Ziehau 
3259dc13fee6SSepherosa Ziehau 	/* Update temporary stats for later use. */
3260dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts++;
3261dc13fee6SSepherosa Ziehau 	txr->hn_stat_size += m_head->m_pkthdr.len;
3262dc13fee6SSepherosa Ziehau 	if (m_head->m_flags & M_MCAST)
3263dc13fee6SSepherosa Ziehau 		txr->hn_stat_mcasts++;
3264dc13fee6SSepherosa Ziehau 
326515516c77SSepherosa Ziehau 	return 0;
326615516c77SSepherosa Ziehau }
326715516c77SSepherosa Ziehau 
326815516c77SSepherosa Ziehau /*
326915516c77SSepherosa Ziehau  * NOTE:
327015516c77SSepherosa Ziehau  * If this function fails, then txd will be freed, but the mbuf
327115516c77SSepherosa Ziehau  * associated w/ the txd will _not_ be freed.
327215516c77SSepherosa Ziehau  */
327315516c77SSepherosa Ziehau static int
327415516c77SSepherosa Ziehau hn_txpkt(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd)
327515516c77SSepherosa Ziehau {
32768e7d3136SSepherosa Ziehau 	int error, send_failed = 0, has_bpf;
327715516c77SSepherosa Ziehau 
327815516c77SSepherosa Ziehau again:
32798e7d3136SSepherosa Ziehau 	has_bpf = bpf_peers_present(ifp->if_bpf);
32808e7d3136SSepherosa Ziehau 	if (has_bpf) {
328115516c77SSepherosa Ziehau 		/*
32828e7d3136SSepherosa Ziehau 		 * Make sure that this txd and any aggregated txds are not
32838e7d3136SSepherosa Ziehau 		 * freed before ETHER_BPF_MTAP.
328415516c77SSepherosa Ziehau 		 */
328515516c77SSepherosa Ziehau 		hn_txdesc_hold(txd);
32868e7d3136SSepherosa Ziehau 	}
328715516c77SSepherosa Ziehau 	error = txr->hn_sendpkt(txr, txd);
328815516c77SSepherosa Ziehau 	if (!error) {
32898e7d3136SSepherosa Ziehau 		if (has_bpf) {
3290dc13fee6SSepherosa Ziehau 			const struct hn_txdesc *tmp_txd;
3291dc13fee6SSepherosa Ziehau 
329215516c77SSepherosa Ziehau 			ETHER_BPF_MTAP(ifp, txd->m);
3293dc13fee6SSepherosa Ziehau 			STAILQ_FOREACH(tmp_txd, &txd->agg_list, agg_link)
3294dc13fee6SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, tmp_txd->m);
3295dc13fee6SSepherosa Ziehau 		}
3296dc13fee6SSepherosa Ziehau 
3297dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, txr->hn_stat_pkts);
329823bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
329923bf9e15SSepherosa Ziehau 		if (!hn_use_if_start)
330023bf9e15SSepherosa Ziehau #endif
330123bf9e15SSepherosa Ziehau 		{
330215516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OBYTES,
3303dc13fee6SSepherosa Ziehau 			    txr->hn_stat_size);
3304dc13fee6SSepherosa Ziehau 			if (txr->hn_stat_mcasts != 0) {
3305dc13fee6SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OMCASTS,
3306dc13fee6SSepherosa Ziehau 				    txr->hn_stat_mcasts);
330715516c77SSepherosa Ziehau 			}
3308dc13fee6SSepherosa Ziehau 		}
3309dc13fee6SSepherosa Ziehau 		txr->hn_pkts += txr->hn_stat_pkts;
3310dc13fee6SSepherosa Ziehau 		txr->hn_sends++;
331115516c77SSepherosa Ziehau 	}
33128e7d3136SSepherosa Ziehau 	if (has_bpf)
331315516c77SSepherosa Ziehau 		hn_txdesc_put(txr, txd);
331415516c77SSepherosa Ziehau 
331515516c77SSepherosa Ziehau 	if (__predict_false(error)) {
331615516c77SSepherosa Ziehau 		int freed;
331715516c77SSepherosa Ziehau 
331815516c77SSepherosa Ziehau 		/*
331915516c77SSepherosa Ziehau 		 * This should "really rarely" happen.
332015516c77SSepherosa Ziehau 		 *
332115516c77SSepherosa Ziehau 		 * XXX Too many RX to be acked or too many sideband
332215516c77SSepherosa Ziehau 		 * commands to run?  Ask netvsc_channel_rollup()
332315516c77SSepherosa Ziehau 		 * to kick start later.
332415516c77SSepherosa Ziehau 		 */
332515516c77SSepherosa Ziehau 		txr->hn_has_txeof = 1;
332615516c77SSepherosa Ziehau 		if (!send_failed) {
332715516c77SSepherosa Ziehau 			txr->hn_send_failed++;
332815516c77SSepherosa Ziehau 			send_failed = 1;
332915516c77SSepherosa Ziehau 			/*
333015516c77SSepherosa Ziehau 			 * Try sending again after set hn_has_txeof;
333115516c77SSepherosa Ziehau 			 * in case that we missed the last
333215516c77SSepherosa Ziehau 			 * netvsc_channel_rollup().
333315516c77SSepherosa Ziehau 			 */
333415516c77SSepherosa Ziehau 			goto again;
333515516c77SSepherosa Ziehau 		}
333615516c77SSepherosa Ziehau 		if_printf(ifp, "send failed\n");
333715516c77SSepherosa Ziehau 
333815516c77SSepherosa Ziehau 		/*
333915516c77SSepherosa Ziehau 		 * Caller will perform further processing on the
334015516c77SSepherosa Ziehau 		 * associated mbuf, so don't free it in hn_txdesc_put();
334115516c77SSepherosa Ziehau 		 * only unload it from the DMA map in hn_txdesc_put(),
334215516c77SSepherosa Ziehau 		 * if it was loaded.
334315516c77SSepherosa Ziehau 		 */
334415516c77SSepherosa Ziehau 		txd->m = NULL;
334515516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
334615516c77SSepherosa Ziehau 		KASSERT(freed != 0,
334715516c77SSepherosa Ziehau 		    ("fail to free txd upon send error"));
334815516c77SSepherosa Ziehau 
334915516c77SSepherosa Ziehau 		txr->hn_send_failed++;
335015516c77SSepherosa Ziehau 	}
3351dc13fee6SSepherosa Ziehau 
3352dc13fee6SSepherosa Ziehau 	/* Reset temporary stats, after this sending is done. */
3353dc13fee6SSepherosa Ziehau 	txr->hn_stat_size = 0;
3354dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts = 0;
3355dc13fee6SSepherosa Ziehau 	txr->hn_stat_mcasts = 0;
3356dc13fee6SSepherosa Ziehau 
3357dc13fee6SSepherosa Ziehau 	return (error);
335815516c77SSepherosa Ziehau }
335915516c77SSepherosa Ziehau 
336015516c77SSepherosa Ziehau /*
336115516c77SSepherosa Ziehau  * Append the specified data to the indicated mbuf chain,
336215516c77SSepherosa Ziehau  * Extend the mbuf chain if the new data does not fit in
336315516c77SSepherosa Ziehau  * existing space.
336415516c77SSepherosa Ziehau  *
336515516c77SSepherosa Ziehau  * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
336615516c77SSepherosa Ziehau  * There should be an equivalent in the kernel mbuf code,
336715516c77SSepherosa Ziehau  * but there does not appear to be one yet.
336815516c77SSepherosa Ziehau  *
336915516c77SSepherosa Ziehau  * Differs from m_append() in that additional mbufs are
337015516c77SSepherosa Ziehau  * allocated with cluster size MJUMPAGESIZE, and filled
337115516c77SSepherosa Ziehau  * accordingly.
337215516c77SSepherosa Ziehau  *
3373a491581fSWei Hu  * Return the last mbuf in the chain or NULL if failed to
3374a491581fSWei Hu  * allocate new mbuf.
337515516c77SSepherosa Ziehau  */
3376a491581fSWei Hu static struct mbuf *
337715516c77SSepherosa Ziehau hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
337815516c77SSepherosa Ziehau {
337915516c77SSepherosa Ziehau 	struct mbuf *m, *n;
338015516c77SSepherosa Ziehau 	int remainder, space;
338115516c77SSepherosa Ziehau 
338215516c77SSepherosa Ziehau 	for (m = m0; m->m_next != NULL; m = m->m_next)
338315516c77SSepherosa Ziehau 		;
338415516c77SSepherosa Ziehau 	remainder = len;
338515516c77SSepherosa Ziehau 	space = M_TRAILINGSPACE(m);
338615516c77SSepherosa Ziehau 	if (space > 0) {
338715516c77SSepherosa Ziehau 		/*
338815516c77SSepherosa Ziehau 		 * Copy into available space.
338915516c77SSepherosa Ziehau 		 */
339015516c77SSepherosa Ziehau 		if (space > remainder)
339115516c77SSepherosa Ziehau 			space = remainder;
339215516c77SSepherosa Ziehau 		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
339315516c77SSepherosa Ziehau 		m->m_len += space;
339415516c77SSepherosa Ziehau 		cp += space;
339515516c77SSepherosa Ziehau 		remainder -= space;
339615516c77SSepherosa Ziehau 	}
339715516c77SSepherosa Ziehau 	while (remainder > 0) {
339815516c77SSepherosa Ziehau 		/*
339915516c77SSepherosa Ziehau 		 * Allocate a new mbuf; could check space
340015516c77SSepherosa Ziehau 		 * and allocate a cluster instead.
340115516c77SSepherosa Ziehau 		 */
340215516c77SSepherosa Ziehau 		n = m_getjcl(M_NOWAIT, m->m_type, 0, MJUMPAGESIZE);
340315516c77SSepherosa Ziehau 		if (n == NULL)
3404a491581fSWei Hu 			return NULL;
340515516c77SSepherosa Ziehau 		n->m_len = min(MJUMPAGESIZE, remainder);
340615516c77SSepherosa Ziehau 		bcopy(cp, mtod(n, caddr_t), n->m_len);
340715516c77SSepherosa Ziehau 		cp += n->m_len;
340815516c77SSepherosa Ziehau 		remainder -= n->m_len;
340915516c77SSepherosa Ziehau 		m->m_next = n;
341015516c77SSepherosa Ziehau 		m = n;
341115516c77SSepherosa Ziehau 	}
341215516c77SSepherosa Ziehau 
3413a491581fSWei Hu 	return m;
341415516c77SSepherosa Ziehau }
341515516c77SSepherosa Ziehau 
341615516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
341715516c77SSepherosa Ziehau static __inline int
341815516c77SSepherosa Ziehau hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m)
341915516c77SSepherosa Ziehau {
342015516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
342115516c77SSepherosa Ziehau 	if (hn_lro_mbufq_depth) {
342215516c77SSepherosa Ziehau 		tcp_lro_queue_mbuf(lc, m);
342315516c77SSepherosa Ziehau 		return 0;
342415516c77SSepherosa Ziehau 	}
342515516c77SSepherosa Ziehau #endif
342615516c77SSepherosa Ziehau 	return tcp_lro_rx(lc, m, 0);
342715516c77SSepherosa Ziehau }
342815516c77SSepherosa Ziehau #endif
342915516c77SSepherosa Ziehau 
343015516c77SSepherosa Ziehau static int
3431a491581fSWei Hu hn_rxpkt(struct hn_rx_ring *rxr)
343215516c77SSepherosa Ziehau {
3433a97fff19SSepherosa Ziehau 	struct ifnet *ifp, *hn_ifp = rxr->hn_ifp;
3434a491581fSWei Hu 	struct mbuf *m_new, *n;
3435642ec226SSepherosa Ziehau 	int size, do_lro = 0, do_csum = 1, is_vf = 0;
3436642ec226SSepherosa Ziehau 	int hash_type = M_HASHTYPE_NONE;
3437db76829bSSepherosa Ziehau 	int l3proto = ETHERTYPE_MAX, l4proto = IPPROTO_DONE;
3438a491581fSWei Hu 	int i;
343915516c77SSepherosa Ziehau 
3440642ec226SSepherosa Ziehau 	ifp = hn_ifp;
3441642ec226SSepherosa Ziehau 	if (rxr->hn_rxvf_ifp != NULL) {
3442a97fff19SSepherosa Ziehau 		/*
3443642ec226SSepherosa Ziehau 		 * Non-transparent mode VF; pretend this packet is from
3444642ec226SSepherosa Ziehau 		 * the VF.
3445a97fff19SSepherosa Ziehau 		 */
3446642ec226SSepherosa Ziehau 		ifp = rxr->hn_rxvf_ifp;
3447642ec226SSepherosa Ziehau 		is_vf = 1;
3448642ec226SSepherosa Ziehau 	} else if (rxr->hn_rx_flags & HN_RX_FLAG_XPNT_VF) {
3449642ec226SSepherosa Ziehau 		/* Transparent mode VF. */
3450642ec226SSepherosa Ziehau 		is_vf = 1;
3451642ec226SSepherosa Ziehau 	}
34525bdfd3fdSDexuan Cui 
3453b3b75d9cSSepherosa Ziehau 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
3454b3b75d9cSSepherosa Ziehau 		/*
3455b3b75d9cSSepherosa Ziehau 		 * NOTE:
3456b3b75d9cSSepherosa Ziehau 		 * See the NOTE of hn_rndis_init_fixat().  This
3457b3b75d9cSSepherosa Ziehau 		 * function can be reached, immediately after the
3458b3b75d9cSSepherosa Ziehau 		 * RNDIS is initialized but before the ifnet is
3459b3b75d9cSSepherosa Ziehau 		 * setup on the hn_attach() path; drop the unexpected
3460b3b75d9cSSepherosa Ziehau 		 * packets.
3461b3b75d9cSSepherosa Ziehau 		 */
3462b3b75d9cSSepherosa Ziehau 		return (0);
3463b3b75d9cSSepherosa Ziehau 	}
3464b3b75d9cSSepherosa Ziehau 
3465a491581fSWei Hu 	if (__predict_false(rxr->rsc.pktlen < ETHER_HDR_LEN)) {
3466a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IERRORS, 1);
3467a97fff19SSepherosa Ziehau 		return (0);
3468a97fff19SSepherosa Ziehau 	}
3469a97fff19SSepherosa Ziehau 
3470a491581fSWei Hu 	if (rxr->rsc.cnt == 1 && rxr->rsc.pktlen <= MHLEN) {
347115516c77SSepherosa Ziehau 		m_new = m_gethdr(M_NOWAIT, MT_DATA);
347215516c77SSepherosa Ziehau 		if (m_new == NULL) {
3473a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
347415516c77SSepherosa Ziehau 			return (0);
347515516c77SSepherosa Ziehau 		}
3476a491581fSWei Hu 		memcpy(mtod(m_new, void *), rxr->rsc.frag_data[0],
3477a491581fSWei Hu 		    rxr->rsc.frag_len[0]);
3478a491581fSWei Hu 		m_new->m_pkthdr.len = m_new->m_len = rxr->rsc.frag_len[0];
347915516c77SSepherosa Ziehau 	} else {
348015516c77SSepherosa Ziehau 		/*
348115516c77SSepherosa Ziehau 		 * Get an mbuf with a cluster.  For packets 2K or less,
348215516c77SSepherosa Ziehau 		 * get a standard 2K cluster.  For anything larger, get a
348315516c77SSepherosa Ziehau 		 * 4K cluster.  Any buffers larger than 4K can cause problems
348415516c77SSepherosa Ziehau 		 * if looped around to the Hyper-V TX channel, so avoid them.
348515516c77SSepherosa Ziehau 		 */
348615516c77SSepherosa Ziehau 		size = MCLBYTES;
3487a491581fSWei Hu 		if (rxr->rsc.pktlen > MCLBYTES) {
348815516c77SSepherosa Ziehau 			/* 4096 */
348915516c77SSepherosa Ziehau 			size = MJUMPAGESIZE;
349015516c77SSepherosa Ziehau 		}
349115516c77SSepherosa Ziehau 
349215516c77SSepherosa Ziehau 		m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size);
349315516c77SSepherosa Ziehau 		if (m_new == NULL) {
3494a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
349515516c77SSepherosa Ziehau 			return (0);
349615516c77SSepherosa Ziehau 		}
349715516c77SSepherosa Ziehau 
3498a491581fSWei Hu 		n = m_new;
3499a491581fSWei Hu 		for (i = 0; i < rxr->rsc.cnt; i++) {
3500a491581fSWei Hu 			n = hv_m_append(n, rxr->rsc.frag_len[i],
3501a491581fSWei Hu 			    rxr->rsc.frag_data[i]);
3502a491581fSWei Hu 			if (n == NULL) {
3503a491581fSWei Hu 				if_inc_counter(hn_ifp, IFCOUNTER_IQDROPS, 1);
3504a491581fSWei Hu 				return (0);
3505a491581fSWei Hu 			} else {
3506a491581fSWei Hu 				m_new->m_pkthdr.len += rxr->rsc.frag_len[i];
350715516c77SSepherosa Ziehau 			}
3508a491581fSWei Hu 		}
3509a491581fSWei Hu 	}
3510a491581fSWei Hu 	if (rxr->rsc.pktlen <= MHLEN)
3511a491581fSWei Hu 		rxr->hn_small_pkts++;
3512a491581fSWei Hu 
351315516c77SSepherosa Ziehau 	m_new->m_pkthdr.rcvif = ifp;
351415516c77SSepherosa Ziehau 
3515a97fff19SSepherosa Ziehau 	if (__predict_false((hn_ifp->if_capenable & IFCAP_RXCSUM) == 0))
351615516c77SSepherosa Ziehau 		do_csum = 0;
351715516c77SSepherosa Ziehau 
351815516c77SSepherosa Ziehau 	/* receive side checksum offload */
3519a491581fSWei Hu 	if (rxr->rsc.csum_info != NULL) {
352015516c77SSepherosa Ziehau 		/* IP csum offload */
3521a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) & NDIS_RXCSUM_INFO_IPCS_OK) && do_csum) {
352215516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
352315516c77SSepherosa Ziehau 			    (CSUM_IP_CHECKED | CSUM_IP_VALID);
352415516c77SSepherosa Ziehau 			rxr->hn_csum_ip++;
352515516c77SSepherosa Ziehau 		}
352615516c77SSepherosa Ziehau 
352715516c77SSepherosa Ziehau 		/* TCP/UDP csum offload */
3528a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) & (NDIS_RXCSUM_INFO_UDPCS_OK |
352915516c77SSepherosa Ziehau 		     NDIS_RXCSUM_INFO_TCPCS_OK)) && do_csum) {
353015516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
353115516c77SSepherosa Ziehau 			    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
353215516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_data = 0xffff;
3533a491581fSWei Hu 			if (*(rxr->rsc.csum_info) & NDIS_RXCSUM_INFO_TCPCS_OK)
353415516c77SSepherosa Ziehau 				rxr->hn_csum_tcp++;
353515516c77SSepherosa Ziehau 			else
353615516c77SSepherosa Ziehau 				rxr->hn_csum_udp++;
353715516c77SSepherosa Ziehau 		}
353815516c77SSepherosa Ziehau 
353915516c77SSepherosa Ziehau 		/*
354015516c77SSepherosa Ziehau 		 * XXX
354115516c77SSepherosa Ziehau 		 * As of this write (Oct 28th, 2016), host side will turn
354215516c77SSepherosa Ziehau 		 * on only TCPCS_OK and IPCS_OK even for UDP datagrams, so
354315516c77SSepherosa Ziehau 		 * the do_lro setting here is actually _not_ accurate.  We
354415516c77SSepherosa Ziehau 		 * depend on the RSS hash type check to reset do_lro.
354515516c77SSepherosa Ziehau 		 */
3546a491581fSWei Hu 		if ((*(rxr->rsc.csum_info) &
354715516c77SSepherosa Ziehau 		     (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) ==
354815516c77SSepherosa Ziehau 		    (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK))
354915516c77SSepherosa Ziehau 			do_lro = 1;
355015516c77SSepherosa Ziehau 	} else {
3551db76829bSSepherosa Ziehau 		hn_rxpkt_proto(m_new, &l3proto, &l4proto);
3552db76829bSSepherosa Ziehau 		if (l3proto == ETHERTYPE_IP) {
3553db76829bSSepherosa Ziehau 			if (l4proto == IPPROTO_TCP) {
355415516c77SSepherosa Ziehau 				if (do_csum &&
355515516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
355615516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_TCP)) {
355715516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
355815516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
355915516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
356015516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
356115516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
356215516c77SSepherosa Ziehau 				}
356315516c77SSepherosa Ziehau 				do_lro = 1;
3564db76829bSSepherosa Ziehau 			} else if (l4proto == IPPROTO_UDP) {
356515516c77SSepherosa Ziehau 				if (do_csum &&
356615516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
356715516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_UDP)) {
356815516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
356915516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
357015516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
357115516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
357215516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
357315516c77SSepherosa Ziehau 				}
3574db76829bSSepherosa Ziehau 			} else if (l4proto != IPPROTO_DONE && do_csum &&
357515516c77SSepherosa Ziehau 			    (rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) {
357615516c77SSepherosa Ziehau 				rxr->hn_csum_trusted++;
357715516c77SSepherosa Ziehau 				m_new->m_pkthdr.csum_flags |=
357815516c77SSepherosa Ziehau 				    (CSUM_IP_CHECKED | CSUM_IP_VALID);
357915516c77SSepherosa Ziehau 			}
358015516c77SSepherosa Ziehau 		}
358115516c77SSepherosa Ziehau 	}
3582db76829bSSepherosa Ziehau 
3583a491581fSWei Hu 	if (rxr->rsc.vlan_info != NULL) {
358415516c77SSepherosa Ziehau 		m_new->m_pkthdr.ether_vtag = EVL_MAKETAG(
3585a491581fSWei Hu 		    NDIS_VLAN_INFO_ID(*(rxr->rsc.vlan_info)),
3586a491581fSWei Hu 		    NDIS_VLAN_INFO_PRI(*(rxr->rsc.vlan_info)),
3587a491581fSWei Hu 		    NDIS_VLAN_INFO_CFI(*(rxr->rsc.vlan_info)));
358815516c77SSepherosa Ziehau 		m_new->m_flags |= M_VLANTAG;
358915516c77SSepherosa Ziehau 	}
359015516c77SSepherosa Ziehau 
3591a97fff19SSepherosa Ziehau 	/*
3592a97fff19SSepherosa Ziehau 	 * If VF is activated (tranparent/non-transparent mode does not
3593a97fff19SSepherosa Ziehau 	 * matter here).
3594a97fff19SSepherosa Ziehau 	 *
3595a97fff19SSepherosa Ziehau 	 * - Disable LRO
3596a97fff19SSepherosa Ziehau 	 *
3597a97fff19SSepherosa Ziehau 	 *   hn(4) will only receive broadcast packets, multicast packets,
3598a97fff19SSepherosa Ziehau 	 *   TCP SYN and SYN|ACK (in Azure), LRO is useless for these
3599a97fff19SSepherosa Ziehau 	 *   packet types.
3600a97fff19SSepherosa Ziehau 	 *
3601a97fff19SSepherosa Ziehau 	 *   For non-transparent, we definitely _cannot_ enable LRO at
3602a97fff19SSepherosa Ziehau 	 *   all, since the LRO flush will use hn(4) as the receiving
3603a97fff19SSepherosa Ziehau 	 *   interface; i.e. hn_ifp->if_input(hn_ifp, m).
3604a97fff19SSepherosa Ziehau 	 */
3605642ec226SSepherosa Ziehau 	if (is_vf)
3606642ec226SSepherosa Ziehau 		do_lro = 0;
3607a97fff19SSepherosa Ziehau 
3608642ec226SSepherosa Ziehau 	/*
3609642ec226SSepherosa Ziehau 	 * If VF is activated (tranparent/non-transparent mode does not
3610642ec226SSepherosa Ziehau 	 * matter here), do _not_ mess with unsupported hash types or
3611642ec226SSepherosa Ziehau 	 * functions.
3612642ec226SSepherosa Ziehau 	 */
3613a491581fSWei Hu 	if (rxr->rsc.hash_info != NULL) {
361415516c77SSepherosa Ziehau 		rxr->hn_rss_pkts++;
3615a491581fSWei Hu 		m_new->m_pkthdr.flowid = *(rxr->rsc.hash_value);
3616642ec226SSepherosa Ziehau 		if (!is_vf)
361715516c77SSepherosa Ziehau 			hash_type = M_HASHTYPE_OPAQUE_HASH;
3618a491581fSWei Hu 		if ((*(rxr->rsc.hash_info) & NDIS_HASH_FUNCTION_MASK) ==
361915516c77SSepherosa Ziehau 		    NDIS_HASH_FUNCTION_TOEPLITZ) {
3620a491581fSWei Hu 			uint32_t type = (*(rxr->rsc.hash_info) & NDIS_HASH_TYPE_MASK &
3621642ec226SSepherosa Ziehau 			    rxr->hn_mbuf_hash);
362215516c77SSepherosa Ziehau 
362315516c77SSepherosa Ziehau 			/*
362415516c77SSepherosa Ziehau 			 * NOTE:
362515516c77SSepherosa Ziehau 			 * do_lro is resetted, if the hash types are not TCP
362615516c77SSepherosa Ziehau 			 * related.  See the comment in the above csum_flags
362715516c77SSepherosa Ziehau 			 * setup section.
362815516c77SSepherosa Ziehau 			 */
362915516c77SSepherosa Ziehau 			switch (type) {
363015516c77SSepherosa Ziehau 			case NDIS_HASH_IPV4:
363115516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV4;
363215516c77SSepherosa Ziehau 				do_lro = 0;
363315516c77SSepherosa Ziehau 				break;
363415516c77SSepherosa Ziehau 
363515516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV4:
363615516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV4;
3637db76829bSSepherosa Ziehau 				if (rxr->hn_rx_flags & HN_RX_FLAG_UDP_HASH) {
3638db76829bSSepherosa Ziehau 					int def_htype = M_HASHTYPE_OPAQUE_HASH;
3639db76829bSSepherosa Ziehau 
3640db76829bSSepherosa Ziehau 					if (is_vf)
3641db76829bSSepherosa Ziehau 						def_htype = M_HASHTYPE_NONE;
3642db76829bSSepherosa Ziehau 
3643db76829bSSepherosa Ziehau 					/*
3644db76829bSSepherosa Ziehau 					 * UDP 4-tuple hash is delivered as
3645db76829bSSepherosa Ziehau 					 * TCP 4-tuple hash.
3646db76829bSSepherosa Ziehau 					 */
3647db76829bSSepherosa Ziehau 					if (l3proto == ETHERTYPE_MAX) {
3648db76829bSSepherosa Ziehau 						hn_rxpkt_proto(m_new,
3649db76829bSSepherosa Ziehau 						    &l3proto, &l4proto);
3650db76829bSSepherosa Ziehau 					}
3651db76829bSSepherosa Ziehau 					if (l3proto == ETHERTYPE_IP) {
36526f12c42eSSepherosa Ziehau 						if (l4proto == IPPROTO_UDP &&
36536f12c42eSSepherosa Ziehau 						    (rxr->hn_mbuf_hash &
36546f12c42eSSepherosa Ziehau 						     NDIS_HASH_UDP_IPV4_X)) {
3655db76829bSSepherosa Ziehau 							hash_type =
3656db76829bSSepherosa Ziehau 							M_HASHTYPE_RSS_UDP_IPV4;
3657db76829bSSepherosa Ziehau 							do_lro = 0;
3658db76829bSSepherosa Ziehau 						} else if (l4proto !=
3659db76829bSSepherosa Ziehau 						    IPPROTO_TCP) {
3660db76829bSSepherosa Ziehau 							hash_type = def_htype;
3661db76829bSSepherosa Ziehau 							do_lro = 0;
3662db76829bSSepherosa Ziehau 						}
3663db76829bSSepherosa Ziehau 					} else {
3664db76829bSSepherosa Ziehau 						hash_type = def_htype;
3665db76829bSSepherosa Ziehau 						do_lro = 0;
3666db76829bSSepherosa Ziehau 					}
3667db76829bSSepherosa Ziehau 				}
366815516c77SSepherosa Ziehau 				break;
366915516c77SSepherosa Ziehau 
367015516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6:
367115516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6;
367215516c77SSepherosa Ziehau 				do_lro = 0;
367315516c77SSepherosa Ziehau 				break;
367415516c77SSepherosa Ziehau 
367515516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6_EX:
367615516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6_EX;
367715516c77SSepherosa Ziehau 				do_lro = 0;
367815516c77SSepherosa Ziehau 				break;
367915516c77SSepherosa Ziehau 
368015516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6:
368115516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6;
368215516c77SSepherosa Ziehau 				break;
368315516c77SSepherosa Ziehau 
368415516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6_EX:
368515516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6_EX;
368615516c77SSepherosa Ziehau 				break;
368715516c77SSepherosa Ziehau 			}
368815516c77SSepherosa Ziehau 		}
3689642ec226SSepherosa Ziehau 	} else if (!is_vf) {
369015516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = rxr->hn_rx_idx;
369115516c77SSepherosa Ziehau 		hash_type = M_HASHTYPE_OPAQUE;
369215516c77SSepherosa Ziehau 	}
369315516c77SSepherosa Ziehau 	M_HASHTYPE_SET(m_new, hash_type);
369415516c77SSepherosa Ziehau 
3695a97fff19SSepherosa Ziehau 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
3696a97fff19SSepherosa Ziehau 	if (hn_ifp != ifp) {
3697a97fff19SSepherosa Ziehau 		const struct ether_header *eh;
3698a97fff19SSepherosa Ziehau 
369915516c77SSepherosa Ziehau 		/*
3700a97fff19SSepherosa Ziehau 		 * Non-transparent mode VF is activated.
370115516c77SSepherosa Ziehau 		 */
370215516c77SSepherosa Ziehau 
3703a97fff19SSepherosa Ziehau 		/*
3704a97fff19SSepherosa Ziehau 		 * Allow tapping on hn(4).
3705a97fff19SSepherosa Ziehau 		 */
3706a97fff19SSepherosa Ziehau 		ETHER_BPF_MTAP(hn_ifp, m_new);
3707a97fff19SSepherosa Ziehau 
3708a97fff19SSepherosa Ziehau 		/*
3709a97fff19SSepherosa Ziehau 		 * Update hn(4)'s stats.
3710a97fff19SSepherosa Ziehau 		 */
3711a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
3712a97fff19SSepherosa Ziehau 		if_inc_counter(hn_ifp, IFCOUNTER_IBYTES, m_new->m_pkthdr.len);
3713a97fff19SSepherosa Ziehau 		/* Checked at the beginning of this function. */
3714a97fff19SSepherosa Ziehau 		KASSERT(m_new->m_len >= ETHER_HDR_LEN, ("not ethernet frame"));
3715a97fff19SSepherosa Ziehau 		eh = mtod(m_new, struct ether_header *);
3716a97fff19SSepherosa Ziehau 		if (ETHER_IS_MULTICAST(eh->ether_dhost))
3717a97fff19SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IMCASTS, 1);
3718a97fff19SSepherosa Ziehau 	}
371915516c77SSepherosa Ziehau 	rxr->hn_pkts++;
372015516c77SSepherosa Ziehau 
3721a97fff19SSepherosa Ziehau 	if ((hn_ifp->if_capenable & IFCAP_LRO) && do_lro) {
372215516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
372315516c77SSepherosa Ziehau 		struct lro_ctrl *lro = &rxr->hn_lro;
372415516c77SSepherosa Ziehau 
372515516c77SSepherosa Ziehau 		if (lro->lro_cnt) {
372615516c77SSepherosa Ziehau 			rxr->hn_lro_tried++;
372715516c77SSepherosa Ziehau 			if (hn_lro_rx(lro, m_new) == 0) {
372815516c77SSepherosa Ziehau 				/* DONE! */
372915516c77SSepherosa Ziehau 				return 0;
373015516c77SSepherosa Ziehau 			}
373115516c77SSepherosa Ziehau 		}
373215516c77SSepherosa Ziehau #endif
373315516c77SSepherosa Ziehau 	}
3734a97fff19SSepherosa Ziehau 	ifp->if_input(ifp, m_new);
373515516c77SSepherosa Ziehau 
373615516c77SSepherosa Ziehau 	return (0);
373715516c77SSepherosa Ziehau }
373815516c77SSepherosa Ziehau 
373915516c77SSepherosa Ziehau static int
374015516c77SSepherosa Ziehau hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
374115516c77SSepherosa Ziehau {
374215516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
37439c6cae24SSepherosa Ziehau 	struct ifreq *ifr = (struct ifreq *)data, ifr_vf;
37449c6cae24SSepherosa Ziehau 	struct ifnet *vf_ifp;
374515516c77SSepherosa Ziehau 	int mask, error = 0;
37468c068aa5SSepherosa Ziehau 	struct ifrsskey *ifrk;
37478c068aa5SSepherosa Ziehau 	struct ifrsshash *ifrh;
3748eb2fe044SSepherosa Ziehau 	uint32_t mtu;
374915516c77SSepherosa Ziehau 
375015516c77SSepherosa Ziehau 	switch (cmd) {
375115516c77SSepherosa Ziehau 	case SIOCSIFMTU:
375215516c77SSepherosa Ziehau 		if (ifr->ifr_mtu > HN_MTU_MAX) {
375315516c77SSepherosa Ziehau 			error = EINVAL;
375415516c77SSepherosa Ziehau 			break;
375515516c77SSepherosa Ziehau 		}
375615516c77SSepherosa Ziehau 
375715516c77SSepherosa Ziehau 		HN_LOCK(sc);
375815516c77SSepherosa Ziehau 
375915516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
376015516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
376115516c77SSepherosa Ziehau 			break;
376215516c77SSepherosa Ziehau 		}
376315516c77SSepherosa Ziehau 
376415516c77SSepherosa Ziehau 		if ((sc->hn_caps & HN_CAP_MTU) == 0) {
376515516c77SSepherosa Ziehau 			/* Can't change MTU */
376615516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
376715516c77SSepherosa Ziehau 			error = EOPNOTSUPP;
376815516c77SSepherosa Ziehau 			break;
376915516c77SSepherosa Ziehau 		}
377015516c77SSepherosa Ziehau 
377115516c77SSepherosa Ziehau 		if (ifp->if_mtu == ifr->ifr_mtu) {
377215516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
377315516c77SSepherosa Ziehau 			break;
377415516c77SSepherosa Ziehau 		}
377515516c77SSepherosa Ziehau 
37769c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
37779c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
37789c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
37799c6cae24SSepherosa Ziehau 			strlcpy(ifr_vf.ifr_name, vf_ifp->if_xname,
37809c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
37819c6cae24SSepherosa Ziehau 			error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFMTU,
37829c6cae24SSepherosa Ziehau 			    (caddr_t)&ifr_vf);
37839c6cae24SSepherosa Ziehau 			if (error) {
37849c6cae24SSepherosa Ziehau 				HN_UNLOCK(sc);
37859c6cae24SSepherosa Ziehau 				if_printf(ifp, "%s SIOCSIFMTU %d failed: %d\n",
37869c6cae24SSepherosa Ziehau 				    vf_ifp->if_xname, ifr->ifr_mtu, error);
37879c6cae24SSepherosa Ziehau 				break;
37889c6cae24SSepherosa Ziehau 			}
37899c6cae24SSepherosa Ziehau 		}
37909c6cae24SSepherosa Ziehau 
379115516c77SSepherosa Ziehau 		/*
379215516c77SSepherosa Ziehau 		 * Suspend this interface before the synthetic parts
379315516c77SSepherosa Ziehau 		 * are ripped.
379415516c77SSepherosa Ziehau 		 */
379515516c77SSepherosa Ziehau 		hn_suspend(sc);
379615516c77SSepherosa Ziehau 
379715516c77SSepherosa Ziehau 		/*
379815516c77SSepherosa Ziehau 		 * Detach the synthetics parts, i.e. NVS and RNDIS.
379915516c77SSepherosa Ziehau 		 */
380015516c77SSepherosa Ziehau 		hn_synth_detach(sc);
380115516c77SSepherosa Ziehau 
380215516c77SSepherosa Ziehau 		/*
380315516c77SSepherosa Ziehau 		 * Reattach the synthetic parts, i.e. NVS and RNDIS,
380415516c77SSepherosa Ziehau 		 * with the new MTU setting.
380515516c77SSepherosa Ziehau 		 */
380615516c77SSepherosa Ziehau 		error = hn_synth_attach(sc, ifr->ifr_mtu);
380715516c77SSepherosa Ziehau 		if (error) {
380815516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
380915516c77SSepherosa Ziehau 			break;
381015516c77SSepherosa Ziehau 		}
381115516c77SSepherosa Ziehau 
3812eb2fe044SSepherosa Ziehau 		error = hn_rndis_get_mtu(sc, &mtu);
3813eb2fe044SSepherosa Ziehau 		if (error)
3814eb2fe044SSepherosa Ziehau 			mtu = ifr->ifr_mtu;
3815eb2fe044SSepherosa Ziehau 		else if (bootverbose)
3816eb2fe044SSepherosa Ziehau 			if_printf(ifp, "RNDIS mtu %u\n", mtu);
3817eb2fe044SSepherosa Ziehau 
381815516c77SSepherosa Ziehau 		/*
381915516c77SSepherosa Ziehau 		 * Commit the requested MTU, after the synthetic parts
382015516c77SSepherosa Ziehau 		 * have been successfully attached.
382115516c77SSepherosa Ziehau 		 */
3822eb2fe044SSepherosa Ziehau 		if (mtu >= ifr->ifr_mtu) {
3823eb2fe044SSepherosa Ziehau 			mtu = ifr->ifr_mtu;
3824eb2fe044SSepherosa Ziehau 		} else {
3825eb2fe044SSepherosa Ziehau 			if_printf(ifp, "fixup mtu %d -> %u\n",
3826eb2fe044SSepherosa Ziehau 			    ifr->ifr_mtu, mtu);
3827eb2fe044SSepherosa Ziehau 		}
3828eb2fe044SSepherosa Ziehau 		ifp->if_mtu = mtu;
382915516c77SSepherosa Ziehau 
383015516c77SSepherosa Ziehau 		/*
38319c6cae24SSepherosa Ziehau 		 * Synthetic parts' reattach may change the chimney
38329c6cae24SSepherosa Ziehau 		 * sending size; update it.
383315516c77SSepherosa Ziehau 		 */
383415516c77SSepherosa Ziehau 		if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
383515516c77SSepherosa Ziehau 			hn_set_chim_size(sc, sc->hn_chim_szmax);
38369c6cae24SSepherosa Ziehau 
38379c6cae24SSepherosa Ziehau 		/*
38389c6cae24SSepherosa Ziehau 		 * Make sure that various parameters based on MTU are
38399c6cae24SSepherosa Ziehau 		 * still valid, after the MTU change.
38409c6cae24SSepherosa Ziehau 		 */
38419c6cae24SSepherosa Ziehau 		hn_mtu_change_fixup(sc);
384215516c77SSepherosa Ziehau 
384315516c77SSepherosa Ziehau 		/*
384415516c77SSepherosa Ziehau 		 * All done!  Resume the interface now.
384515516c77SSepherosa Ziehau 		 */
384615516c77SSepherosa Ziehau 		hn_resume(sc);
384715516c77SSepherosa Ziehau 
3848d0cd8231SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXVF) ||
3849d0cd8231SSepherosa Ziehau 		    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
38509c6cae24SSepherosa Ziehau 			/*
38519c6cae24SSepherosa Ziehau 			 * Since we have reattached the NVS part,
38529c6cae24SSepherosa Ziehau 			 * change the datapath to VF again; in case
38539c6cae24SSepherosa Ziehau 			 * that it is lost, after the NVS was detached.
38549c6cae24SSepherosa Ziehau 			 */
38559c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
38569c6cae24SSepherosa Ziehau 		}
38579c6cae24SSepherosa Ziehau 
385815516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
385915516c77SSepherosa Ziehau 		break;
386015516c77SSepherosa Ziehau 
386115516c77SSepherosa Ziehau 	case SIOCSIFFLAGS:
386215516c77SSepherosa Ziehau 		HN_LOCK(sc);
386315516c77SSepherosa Ziehau 
386415516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
386515516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
386615516c77SSepherosa Ziehau 			break;
386715516c77SSepherosa Ziehau 		}
386815516c77SSepherosa Ziehau 
38699c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc))
38709c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
38719c6cae24SSepherosa Ziehau 
387215516c77SSepherosa Ziehau 		if (ifp->if_flags & IFF_UP) {
3873fdc4f478SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3874fdc4f478SSepherosa Ziehau 				/*
3875fdc4f478SSepherosa Ziehau 				 * Caller meight hold mutex, e.g.
3876fdc4f478SSepherosa Ziehau 				 * bpf; use busy-wait for the RNDIS
3877fdc4f478SSepherosa Ziehau 				 * reply.
3878fdc4f478SSepherosa Ziehau 				 */
3879fdc4f478SSepherosa Ziehau 				HN_NO_SLEEPING(sc);
3880c08f7b2cSSepherosa Ziehau 				hn_rxfilter_config(sc);
3881fdc4f478SSepherosa Ziehau 				HN_SLEEPING_OK(sc);
38829c6cae24SSepherosa Ziehau 
38839c6cae24SSepherosa Ziehau 				if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
38849c6cae24SSepherosa Ziehau 					error = hn_xpnt_vf_iocsetflags(sc);
3885fdc4f478SSepherosa Ziehau 			} else {
388615516c77SSepherosa Ziehau 				hn_init_locked(sc);
3887fdc4f478SSepherosa Ziehau 			}
388815516c77SSepherosa Ziehau 		} else {
388915516c77SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
38905bdfd3fdSDexuan Cui 				hn_stop(sc, false);
389115516c77SSepherosa Ziehau 		}
389215516c77SSepherosa Ziehau 		sc->hn_if_flags = ifp->if_flags;
389315516c77SSepherosa Ziehau 
389415516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
389515516c77SSepherosa Ziehau 		break;
389615516c77SSepherosa Ziehau 
389715516c77SSepherosa Ziehau 	case SIOCSIFCAP:
389815516c77SSepherosa Ziehau 		HN_LOCK(sc);
38999c6cae24SSepherosa Ziehau 
39009c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
39019c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
39029c6cae24SSepherosa Ziehau 			strlcpy(ifr_vf.ifr_name, sc->hn_vf_ifp->if_xname,
39039c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
39049c6cae24SSepherosa Ziehau 			error = hn_xpnt_vf_iocsetcaps(sc, &ifr_vf);
39059c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
39069c6cae24SSepherosa Ziehau 			break;
39079c6cae24SSepherosa Ziehau 		}
39089c6cae24SSepherosa Ziehau 
39099c6cae24SSepherosa Ziehau 		/*
39109c6cae24SSepherosa Ziehau 		 * Fix up requested capabilities w/ supported capabilities,
39119c6cae24SSepherosa Ziehau 		 * since the supported capabilities could have been changed.
39129c6cae24SSepherosa Ziehau 		 */
39139c6cae24SSepherosa Ziehau 		mask = (ifr->ifr_reqcap & ifp->if_capabilities) ^
39149c6cae24SSepherosa Ziehau 		    ifp->if_capenable;
391515516c77SSepherosa Ziehau 
391615516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM) {
391715516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
391815516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
391915516c77SSepherosa Ziehau 				ifp->if_hwassist |= HN_CSUM_IP_HWASSIST(sc);
392015516c77SSepherosa Ziehau 			else
392115516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~HN_CSUM_IP_HWASSIST(sc);
392215516c77SSepherosa Ziehau 		}
392315516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM_IPV6) {
392415516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM_IPV6;
392515516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
392615516c77SSepherosa Ziehau 				ifp->if_hwassist |= HN_CSUM_IP6_HWASSIST(sc);
392715516c77SSepherosa Ziehau 			else
392815516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~HN_CSUM_IP6_HWASSIST(sc);
392915516c77SSepherosa Ziehau 		}
393015516c77SSepherosa Ziehau 
393115516c77SSepherosa Ziehau 		/* TODO: flip RNDIS offload parameters for RXCSUM. */
393215516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
393315516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
393415516c77SSepherosa Ziehau #ifdef foo
393515516c77SSepherosa Ziehau 		/* We can't diff IPv6 packets from IPv4 packets on RX path. */
393615516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM_IPV6)
393715516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM_IPV6;
393815516c77SSepherosa Ziehau #endif
393915516c77SSepherosa Ziehau 
394015516c77SSepherosa Ziehau 		if (mask & IFCAP_LRO)
394115516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_LRO;
394215516c77SSepherosa Ziehau 
394315516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO4) {
394415516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO4;
394515516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO4)
394615516c77SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_IP_TSO;
394715516c77SSepherosa Ziehau 			else
394815516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_IP_TSO;
394915516c77SSepherosa Ziehau 		}
395015516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO6) {
395115516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO6;
395215516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO6)
395315516c77SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_IP6_TSO;
395415516c77SSepherosa Ziehau 			else
395515516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_IP6_TSO;
395615516c77SSepherosa Ziehau 		}
395715516c77SSepherosa Ziehau 
395815516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
395915516c77SSepherosa Ziehau 		break;
396015516c77SSepherosa Ziehau 
396115516c77SSepherosa Ziehau 	case SIOCADDMULTI:
396215516c77SSepherosa Ziehau 	case SIOCDELMULTI:
396315516c77SSepherosa Ziehau 		HN_LOCK(sc);
396415516c77SSepherosa Ziehau 
396515516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
396615516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
396715516c77SSepherosa Ziehau 			break;
396815516c77SSepherosa Ziehau 		}
3969fdc4f478SSepherosa Ziehau 		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3970fdc4f478SSepherosa Ziehau 			/*
3971fdc4f478SSepherosa Ziehau 			 * Multicast uses mutex; use busy-wait for
3972fdc4f478SSepherosa Ziehau 			 * the RNDIS reply.
3973fdc4f478SSepherosa Ziehau 			 */
3974fdc4f478SSepherosa Ziehau 			HN_NO_SLEEPING(sc);
3975c08f7b2cSSepherosa Ziehau 			hn_rxfilter_config(sc);
3976fdc4f478SSepherosa Ziehau 			HN_SLEEPING_OK(sc);
3977fdc4f478SSepherosa Ziehau 		}
397815516c77SSepherosa Ziehau 
39799c6cae24SSepherosa Ziehau 		/* XXX vlan(4) style mcast addr maintenance */
39809c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
39819c6cae24SSepherosa Ziehau 			int old_if_flags;
39829c6cae24SSepherosa Ziehau 
39839c6cae24SSepherosa Ziehau 			old_if_flags = sc->hn_vf_ifp->if_flags;
39849c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
39859c6cae24SSepherosa Ziehau 
39869c6cae24SSepherosa Ziehau 			if ((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) &&
39879c6cae24SSepherosa Ziehau 			    ((old_if_flags ^ sc->hn_vf_ifp->if_flags) &
39889c6cae24SSepherosa Ziehau 			     IFF_ALLMULTI))
39899c6cae24SSepherosa Ziehau 				error = hn_xpnt_vf_iocsetflags(sc);
39909c6cae24SSepherosa Ziehau 		}
39919c6cae24SSepherosa Ziehau 
399215516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
399315516c77SSepherosa Ziehau 		break;
399415516c77SSepherosa Ziehau 
399515516c77SSepherosa Ziehau 	case SIOCSIFMEDIA:
399615516c77SSepherosa Ziehau 	case SIOCGIFMEDIA:
39979c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
39989c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
39999c6cae24SSepherosa Ziehau 			/*
40009c6cae24SSepherosa Ziehau 			 * SIOCGIFMEDIA expects ifmediareq, so don't
40019c6cae24SSepherosa Ziehau 			 * create and pass ifr_vf to the VF here; just
40029c6cae24SSepherosa Ziehau 			 * replace the ifr_name.
40039c6cae24SSepherosa Ziehau 			 */
40049c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
40059c6cae24SSepherosa Ziehau 			strlcpy(ifr->ifr_name, vf_ifp->if_xname,
40069c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
40079c6cae24SSepherosa Ziehau 			error = vf_ifp->if_ioctl(vf_ifp, cmd, data);
40089c6cae24SSepherosa Ziehau 			/* Restore the ifr_name. */
40099c6cae24SSepherosa Ziehau 			strlcpy(ifr->ifr_name, ifp->if_xname,
40109c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
40119c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
40129c6cae24SSepherosa Ziehau 			break;
40139c6cae24SSepherosa Ziehau 		}
40149c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
401515516c77SSepherosa Ziehau 		error = ifmedia_ioctl(ifp, ifr, &sc->hn_media, cmd);
401615516c77SSepherosa Ziehau 		break;
401715516c77SSepherosa Ziehau 
40188c068aa5SSepherosa Ziehau 	case SIOCGIFRSSHASH:
40198c068aa5SSepherosa Ziehau 		ifrh = (struct ifrsshash *)data;
40208c068aa5SSepherosa Ziehau 		HN_LOCK(sc);
40218c068aa5SSepherosa Ziehau 		if (sc->hn_rx_ring_inuse == 1) {
40228c068aa5SSepherosa Ziehau 			HN_UNLOCK(sc);
40238c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_NONE;
40248c068aa5SSepherosa Ziehau 			ifrh->ifrh_types = 0;
40258c068aa5SSepherosa Ziehau 			break;
40268c068aa5SSepherosa Ziehau 		}
40278c068aa5SSepherosa Ziehau 
40288c068aa5SSepherosa Ziehau 		if (sc->hn_rss_hash & NDIS_HASH_FUNCTION_TOEPLITZ)
40298c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_TOEPLITZ;
40308c068aa5SSepherosa Ziehau 		else
40318c068aa5SSepherosa Ziehau 			ifrh->ifrh_func = RSS_FUNC_PRIVATE;
4032642ec226SSepherosa Ziehau 		ifrh->ifrh_types = hn_rss_type_fromndis(sc->hn_rss_hash);
40338c068aa5SSepherosa Ziehau 		HN_UNLOCK(sc);
40348c068aa5SSepherosa Ziehau 		break;
40358c068aa5SSepherosa Ziehau 
40368c068aa5SSepherosa Ziehau 	case SIOCGIFRSSKEY:
40378c068aa5SSepherosa Ziehau 		ifrk = (struct ifrsskey *)data;
40388c068aa5SSepherosa Ziehau 		HN_LOCK(sc);
40398c068aa5SSepherosa Ziehau 		if (sc->hn_rx_ring_inuse == 1) {
40408c068aa5SSepherosa Ziehau 			HN_UNLOCK(sc);
40418c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_NONE;
40428c068aa5SSepherosa Ziehau 			ifrk->ifrk_keylen = 0;
40438c068aa5SSepherosa Ziehau 			break;
40448c068aa5SSepherosa Ziehau 		}
40458c068aa5SSepherosa Ziehau 		if (sc->hn_rss_hash & NDIS_HASH_FUNCTION_TOEPLITZ)
40468c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_TOEPLITZ;
40478c068aa5SSepherosa Ziehau 		else
40488c068aa5SSepherosa Ziehau 			ifrk->ifrk_func = RSS_FUNC_PRIVATE;
40498c068aa5SSepherosa Ziehau 		ifrk->ifrk_keylen = NDIS_HASH_KEYSIZE_TOEPLITZ;
40508c068aa5SSepherosa Ziehau 		memcpy(ifrk->ifrk_key, sc->hn_rss.rss_key,
40518c068aa5SSepherosa Ziehau 		    NDIS_HASH_KEYSIZE_TOEPLITZ);
40528c068aa5SSepherosa Ziehau 		HN_UNLOCK(sc);
40538c068aa5SSepherosa Ziehau 		break;
40548c068aa5SSepherosa Ziehau 
405515516c77SSepherosa Ziehau 	default:
405615516c77SSepherosa Ziehau 		error = ether_ioctl(ifp, cmd, data);
405715516c77SSepherosa Ziehau 		break;
405815516c77SSepherosa Ziehau 	}
405915516c77SSepherosa Ziehau 	return (error);
406015516c77SSepherosa Ziehau }
406115516c77SSepherosa Ziehau 
406215516c77SSepherosa Ziehau static void
40635bdfd3fdSDexuan Cui hn_stop(struct hn_softc *sc, bool detaching)
406415516c77SSepherosa Ziehau {
406515516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
406615516c77SSepherosa Ziehau 	int i;
406715516c77SSepherosa Ziehau 
406815516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
406915516c77SSepherosa Ziehau 
407015516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
407115516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
407215516c77SSepherosa Ziehau 
40739c6cae24SSepherosa Ziehau 	/* Clear RUNNING bit ASAP. */
40749c6cae24SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
40759c6cae24SSepherosa Ziehau 
40766c1204dfSSepherosa Ziehau 	/* Disable polling. */
40776c1204dfSSepherosa Ziehau 	hn_polling(sc, 0);
40786c1204dfSSepherosa Ziehau 
40799c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
40809c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_ifp != NULL,
40819c6cae24SSepherosa Ziehau 		    ("%s: VF is not attached", ifp->if_xname));
40829c6cae24SSepherosa Ziehau 
4083a97fff19SSepherosa Ziehau 		/* Mark transparent mode VF as disabled. */
4084a97fff19SSepherosa Ziehau 		hn_xpnt_vf_setdisable(sc, false /* keep hn_vf_ifp */);
40859c6cae24SSepherosa Ziehau 
40869c6cae24SSepherosa Ziehau 		/*
40879c6cae24SSepherosa Ziehau 		 * NOTE:
40889c6cae24SSepherosa Ziehau 		 * Datapath setting must happen _before_ bringing
40899c6cae24SSepherosa Ziehau 		 * the VF down.
40909c6cae24SSepherosa Ziehau 		 */
40919c6cae24SSepherosa Ziehau 		hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
40929c6cae24SSepherosa Ziehau 
40939c6cae24SSepherosa Ziehau 		/*
40949c6cae24SSepherosa Ziehau 		 * Bring the VF down.
40959c6cae24SSepherosa Ziehau 		 */
40969c6cae24SSepherosa Ziehau 		hn_xpnt_vf_saveifflags(sc);
40979c6cae24SSepherosa Ziehau 		sc->hn_vf_ifp->if_flags &= ~IFF_UP;
40989c6cae24SSepherosa Ziehau 		hn_xpnt_vf_iocsetflags(sc);
40999c6cae24SSepherosa Ziehau 	}
41009c6cae24SSepherosa Ziehau 
41019c6cae24SSepherosa Ziehau 	/* Suspend data transfers. */
410215516c77SSepherosa Ziehau 	hn_suspend_data(sc);
410315516c77SSepherosa Ziehau 
410415516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
410515516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
410615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
410715516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
41085bdfd3fdSDexuan Cui 
41095bdfd3fdSDexuan Cui 	/*
41109c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is active, make sure
41119c6cae24SSepherosa Ziehau 	 * that the RX filter still allows packet reception.
41125bdfd3fdSDexuan Cui 	 */
4113962f0357SSepherosa Ziehau 	if (!detaching && (sc->hn_flags & HN_FLAG_RXVF))
41145bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
411515516c77SSepherosa Ziehau }
411615516c77SSepherosa Ziehau 
411715516c77SSepherosa Ziehau static void
411815516c77SSepherosa Ziehau hn_init_locked(struct hn_softc *sc)
411915516c77SSepherosa Ziehau {
412015516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
412115516c77SSepherosa Ziehau 	int i;
412215516c77SSepherosa Ziehau 
412315516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
412415516c77SSepherosa Ziehau 
412515516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
412615516c77SSepherosa Ziehau 		return;
412715516c77SSepherosa Ziehau 
412815516c77SSepherosa Ziehau 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
412915516c77SSepherosa Ziehau 		return;
413015516c77SSepherosa Ziehau 
413115516c77SSepherosa Ziehau 	/* Configure RX filter */
4132c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
413315516c77SSepherosa Ziehau 
413415516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
413515516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
413615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
413715516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
413815516c77SSepherosa Ziehau 
413915516c77SSepherosa Ziehau 	/* Clear TX 'suspended' bit. */
414015516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_inuse);
414115516c77SSepherosa Ziehau 
41429c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
41439c6cae24SSepherosa Ziehau 		/* Initialize transparent VF. */
41449c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
41459c6cae24SSepherosa Ziehau 	}
41469c6cae24SSepherosa Ziehau 
414715516c77SSepherosa Ziehau 	/* Everything is ready; unleash! */
414815516c77SSepherosa Ziehau 	atomic_set_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
41496c1204dfSSepherosa Ziehau 
41506c1204dfSSepherosa Ziehau 	/* Re-enable polling if requested. */
41516c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz > 0)
41526c1204dfSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
415315516c77SSepherosa Ziehau }
415415516c77SSepherosa Ziehau 
415515516c77SSepherosa Ziehau static void
415615516c77SSepherosa Ziehau hn_init(void *xsc)
415715516c77SSepherosa Ziehau {
415815516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
415915516c77SSepherosa Ziehau 
416015516c77SSepherosa Ziehau 	HN_LOCK(sc);
416115516c77SSepherosa Ziehau 	hn_init_locked(sc);
416215516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
416315516c77SSepherosa Ziehau }
416415516c77SSepherosa Ziehau 
416515516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
416615516c77SSepherosa Ziehau 
416715516c77SSepherosa Ziehau static int
416815516c77SSepherosa Ziehau hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS)
416915516c77SSepherosa Ziehau {
417015516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
417115516c77SSepherosa Ziehau 	unsigned int lenlim;
417215516c77SSepherosa Ziehau 	int error;
417315516c77SSepherosa Ziehau 
417415516c77SSepherosa Ziehau 	lenlim = sc->hn_rx_ring[0].hn_lro.lro_length_lim;
417515516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &lenlim, 0, req);
417615516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
417715516c77SSepherosa Ziehau 		return error;
417815516c77SSepherosa Ziehau 
417915516c77SSepherosa Ziehau 	HN_LOCK(sc);
418015516c77SSepherosa Ziehau 	if (lenlim < HN_LRO_LENLIM_MIN(sc->hn_ifp) ||
418115516c77SSepherosa Ziehau 	    lenlim > TCP_LRO_LENGTH_MAX) {
418215516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
418315516c77SSepherosa Ziehau 		return EINVAL;
418415516c77SSepherosa Ziehau 	}
418515516c77SSepherosa Ziehau 	hn_set_lro_lenlim(sc, lenlim);
418615516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
418715516c77SSepherosa Ziehau 
418815516c77SSepherosa Ziehau 	return 0;
418915516c77SSepherosa Ziehau }
419015516c77SSepherosa Ziehau 
419115516c77SSepherosa Ziehau static int
419215516c77SSepherosa Ziehau hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS)
419315516c77SSepherosa Ziehau {
419415516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
419515516c77SSepherosa Ziehau 	int ackcnt, error, i;
419615516c77SSepherosa Ziehau 
419715516c77SSepherosa Ziehau 	/*
419815516c77SSepherosa Ziehau 	 * lro_ackcnt_lim is append count limit,
419915516c77SSepherosa Ziehau 	 * +1 to turn it into aggregation limit.
420015516c77SSepherosa Ziehau 	 */
420115516c77SSepherosa Ziehau 	ackcnt = sc->hn_rx_ring[0].hn_lro.lro_ackcnt_lim + 1;
420215516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &ackcnt, 0, req);
420315516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
420415516c77SSepherosa Ziehau 		return error;
420515516c77SSepherosa Ziehau 
420615516c77SSepherosa Ziehau 	if (ackcnt < 2 || ackcnt > (TCP_LRO_ACKCNT_MAX + 1))
420715516c77SSepherosa Ziehau 		return EINVAL;
420815516c77SSepherosa Ziehau 
420915516c77SSepherosa Ziehau 	/*
421015516c77SSepherosa Ziehau 	 * Convert aggregation limit back to append
421115516c77SSepherosa Ziehau 	 * count limit.
421215516c77SSepherosa Ziehau 	 */
421315516c77SSepherosa Ziehau 	--ackcnt;
421415516c77SSepherosa Ziehau 	HN_LOCK(sc);
4215a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
421615516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_ackcnt_lim = ackcnt;
421715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
421815516c77SSepherosa Ziehau 	return 0;
421915516c77SSepherosa Ziehau }
422015516c77SSepherosa Ziehau 
422115516c77SSepherosa Ziehau #endif
422215516c77SSepherosa Ziehau 
422315516c77SSepherosa Ziehau static int
422415516c77SSepherosa Ziehau hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS)
422515516c77SSepherosa Ziehau {
422615516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
422715516c77SSepherosa Ziehau 	int hcsum = arg2;
422815516c77SSepherosa Ziehau 	int on, error, i;
422915516c77SSepherosa Ziehau 
423015516c77SSepherosa Ziehau 	on = 0;
423115516c77SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_trust_hcsum & hcsum)
423215516c77SSepherosa Ziehau 		on = 1;
423315516c77SSepherosa Ziehau 
423415516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &on, 0, req);
423515516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
423615516c77SSepherosa Ziehau 		return error;
423715516c77SSepherosa Ziehau 
423815516c77SSepherosa Ziehau 	HN_LOCK(sc);
4239a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
424015516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
424115516c77SSepherosa Ziehau 
424215516c77SSepherosa Ziehau 		if (on)
424315516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= hcsum;
424415516c77SSepherosa Ziehau 		else
424515516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum &= ~hcsum;
424615516c77SSepherosa Ziehau 	}
424715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
424815516c77SSepherosa Ziehau 	return 0;
424915516c77SSepherosa Ziehau }
425015516c77SSepherosa Ziehau 
425115516c77SSepherosa Ziehau static int
425215516c77SSepherosa Ziehau hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS)
425315516c77SSepherosa Ziehau {
425415516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
425515516c77SSepherosa Ziehau 	int chim_size, error;
425615516c77SSepherosa Ziehau 
425715516c77SSepherosa Ziehau 	chim_size = sc->hn_tx_ring[0].hn_chim_size;
425815516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &chim_size, 0, req);
425915516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
426015516c77SSepherosa Ziehau 		return error;
426115516c77SSepherosa Ziehau 
426215516c77SSepherosa Ziehau 	if (chim_size > sc->hn_chim_szmax || chim_size <= 0)
426315516c77SSepherosa Ziehau 		return EINVAL;
426415516c77SSepherosa Ziehau 
426515516c77SSepherosa Ziehau 	HN_LOCK(sc);
426615516c77SSepherosa Ziehau 	hn_set_chim_size(sc, chim_size);
426715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
426815516c77SSepherosa Ziehau 	return 0;
426915516c77SSepherosa Ziehau }
427015516c77SSepherosa Ziehau 
427115516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
427215516c77SSepherosa Ziehau static int
427315516c77SSepherosa Ziehau hn_rx_stat_int_sysctl(SYSCTL_HANDLER_ARGS)
427415516c77SSepherosa Ziehau {
427515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
427615516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
427715516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
427815516c77SSepherosa Ziehau 	uint64_t stat;
427915516c77SSepherosa Ziehau 
428015516c77SSepherosa Ziehau 	stat = 0;
428115516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
428215516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
428315516c77SSepherosa Ziehau 		stat += *((int *)((uint8_t *)rxr + ofs));
428415516c77SSepherosa Ziehau 	}
428515516c77SSepherosa Ziehau 
428615516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
428715516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
428815516c77SSepherosa Ziehau 		return error;
428915516c77SSepherosa Ziehau 
429015516c77SSepherosa Ziehau 	/* Zero out this stat. */
429115516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
429215516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
429315516c77SSepherosa Ziehau 		*((int *)((uint8_t *)rxr + ofs)) = 0;
429415516c77SSepherosa Ziehau 	}
429515516c77SSepherosa Ziehau 	return 0;
429615516c77SSepherosa Ziehau }
429715516c77SSepherosa Ziehau #else
429815516c77SSepherosa Ziehau static int
429915516c77SSepherosa Ziehau hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS)
430015516c77SSepherosa Ziehau {
430115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
430215516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
430315516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
430415516c77SSepherosa Ziehau 	uint64_t stat;
430515516c77SSepherosa Ziehau 
430615516c77SSepherosa Ziehau 	stat = 0;
4307a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
430815516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
430915516c77SSepherosa Ziehau 		stat += *((uint64_t *)((uint8_t *)rxr + ofs));
431015516c77SSepherosa Ziehau 	}
431115516c77SSepherosa Ziehau 
431215516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
431315516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
431415516c77SSepherosa Ziehau 		return error;
431515516c77SSepherosa Ziehau 
431615516c77SSepherosa Ziehau 	/* Zero out this stat. */
4317a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
431815516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
431915516c77SSepherosa Ziehau 		*((uint64_t *)((uint8_t *)rxr + ofs)) = 0;
432015516c77SSepherosa Ziehau 	}
432115516c77SSepherosa Ziehau 	return 0;
432215516c77SSepherosa Ziehau }
432315516c77SSepherosa Ziehau 
432415516c77SSepherosa Ziehau #endif
432515516c77SSepherosa Ziehau 
432615516c77SSepherosa Ziehau static int
432715516c77SSepherosa Ziehau hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
432815516c77SSepherosa Ziehau {
432915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
433015516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
433115516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
433215516c77SSepherosa Ziehau 	u_long stat;
433315516c77SSepherosa Ziehau 
433415516c77SSepherosa Ziehau 	stat = 0;
4335a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
433615516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
433715516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)rxr + ofs));
433815516c77SSepherosa Ziehau 	}
433915516c77SSepherosa Ziehau 
434015516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
434115516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
434215516c77SSepherosa Ziehau 		return error;
434315516c77SSepherosa Ziehau 
434415516c77SSepherosa Ziehau 	/* Zero out this stat. */
4345a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
434615516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
434715516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)rxr + ofs)) = 0;
434815516c77SSepherosa Ziehau 	}
434915516c77SSepherosa Ziehau 	return 0;
435015516c77SSepherosa Ziehau }
435115516c77SSepherosa Ziehau 
435215516c77SSepherosa Ziehau static int
435315516c77SSepherosa Ziehau hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
435415516c77SSepherosa Ziehau {
435515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
435615516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
435715516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
435815516c77SSepherosa Ziehau 	u_long stat;
435915516c77SSepherosa Ziehau 
436015516c77SSepherosa Ziehau 	stat = 0;
4361a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
436215516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
436315516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)txr + ofs));
436415516c77SSepherosa Ziehau 	}
436515516c77SSepherosa Ziehau 
436615516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
436715516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
436815516c77SSepherosa Ziehau 		return error;
436915516c77SSepherosa Ziehau 
437015516c77SSepherosa Ziehau 	/* Zero out this stat. */
4371a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
437215516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
437315516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)txr + ofs)) = 0;
437415516c77SSepherosa Ziehau 	}
437515516c77SSepherosa Ziehau 	return 0;
437615516c77SSepherosa Ziehau }
437715516c77SSepherosa Ziehau 
437815516c77SSepherosa Ziehau static int
437915516c77SSepherosa Ziehau hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS)
438015516c77SSepherosa Ziehau {
438115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
438215516c77SSepherosa Ziehau 	int ofs = arg2, i, error, conf;
438315516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
438415516c77SSepherosa Ziehau 
438515516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[0];
438615516c77SSepherosa Ziehau 	conf = *((int *)((uint8_t *)txr + ofs));
438715516c77SSepherosa Ziehau 
438815516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &conf, 0, req);
438915516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
439015516c77SSepherosa Ziehau 		return error;
439115516c77SSepherosa Ziehau 
439215516c77SSepherosa Ziehau 	HN_LOCK(sc);
4393a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
439415516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
439515516c77SSepherosa Ziehau 		*((int *)((uint8_t *)txr + ofs)) = conf;
439615516c77SSepherosa Ziehau 	}
439715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
439815516c77SSepherosa Ziehau 
439915516c77SSepherosa Ziehau 	return 0;
440015516c77SSepherosa Ziehau }
440115516c77SSepherosa Ziehau 
440215516c77SSepherosa Ziehau static int
4403dc13fee6SSepherosa Ziehau hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS)
4404dc13fee6SSepherosa Ziehau {
4405dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4406dc13fee6SSepherosa Ziehau 	int error, size;
4407dc13fee6SSepherosa Ziehau 
4408dc13fee6SSepherosa Ziehau 	size = sc->hn_agg_size;
4409dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &size, 0, req);
4410dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
4411dc13fee6SSepherosa Ziehau 		return (error);
4412dc13fee6SSepherosa Ziehau 
4413dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
4414dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = size;
4415dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
4416dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
4417dc13fee6SSepherosa Ziehau 
4418dc13fee6SSepherosa Ziehau 	return (0);
4419dc13fee6SSepherosa Ziehau }
4420dc13fee6SSepherosa Ziehau 
4421dc13fee6SSepherosa Ziehau static int
4422dc13fee6SSepherosa Ziehau hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS)
4423dc13fee6SSepherosa Ziehau {
4424dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4425dc13fee6SSepherosa Ziehau 	int error, pkts;
4426dc13fee6SSepherosa Ziehau 
4427dc13fee6SSepherosa Ziehau 	pkts = sc->hn_agg_pkts;
4428dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pkts, 0, req);
4429dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
4430dc13fee6SSepherosa Ziehau 		return (error);
4431dc13fee6SSepherosa Ziehau 
4432dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
4433dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = pkts;
4434dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
4435dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
4436dc13fee6SSepherosa Ziehau 
4437dc13fee6SSepherosa Ziehau 	return (0);
4438dc13fee6SSepherosa Ziehau }
4439dc13fee6SSepherosa Ziehau 
4440dc13fee6SSepherosa Ziehau static int
4441dc13fee6SSepherosa Ziehau hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS)
4442dc13fee6SSepherosa Ziehau {
4443dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4444dc13fee6SSepherosa Ziehau 	int pkts;
4445dc13fee6SSepherosa Ziehau 
4446dc13fee6SSepherosa Ziehau 	pkts = sc->hn_tx_ring[0].hn_agg_pktmax;
4447dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &pkts, 0, req));
4448dc13fee6SSepherosa Ziehau }
4449dc13fee6SSepherosa Ziehau 
4450dc13fee6SSepherosa Ziehau static int
4451dc13fee6SSepherosa Ziehau hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS)
4452dc13fee6SSepherosa Ziehau {
4453dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4454dc13fee6SSepherosa Ziehau 	int align;
4455dc13fee6SSepherosa Ziehau 
4456dc13fee6SSepherosa Ziehau 	align = sc->hn_tx_ring[0].hn_agg_align;
4457dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &align, 0, req));
4458dc13fee6SSepherosa Ziehau }
4459dc13fee6SSepherosa Ziehau 
44606c1204dfSSepherosa Ziehau static void
44616c1204dfSSepherosa Ziehau hn_chan_polling(struct vmbus_channel *chan, u_int pollhz)
44626c1204dfSSepherosa Ziehau {
44636c1204dfSSepherosa Ziehau 	if (pollhz == 0)
44646c1204dfSSepherosa Ziehau 		vmbus_chan_poll_disable(chan);
44656c1204dfSSepherosa Ziehau 	else
44666c1204dfSSepherosa Ziehau 		vmbus_chan_poll_enable(chan, pollhz);
44676c1204dfSSepherosa Ziehau }
44686c1204dfSSepherosa Ziehau 
44696c1204dfSSepherosa Ziehau static void
44706c1204dfSSepherosa Ziehau hn_polling(struct hn_softc *sc, u_int pollhz)
44716c1204dfSSepherosa Ziehau {
44726c1204dfSSepherosa Ziehau 	int nsubch = sc->hn_rx_ring_inuse - 1;
44736c1204dfSSepherosa Ziehau 
44746c1204dfSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
44756c1204dfSSepherosa Ziehau 
44766c1204dfSSepherosa Ziehau 	if (nsubch > 0) {
44776c1204dfSSepherosa Ziehau 		struct vmbus_channel **subch;
44786c1204dfSSepherosa Ziehau 		int i;
44796c1204dfSSepherosa Ziehau 
44806c1204dfSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
44816c1204dfSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
44826c1204dfSSepherosa Ziehau 			hn_chan_polling(subch[i], pollhz);
44836c1204dfSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
44846c1204dfSSepherosa Ziehau 	}
44856c1204dfSSepherosa Ziehau 	hn_chan_polling(sc->hn_prichan, pollhz);
44866c1204dfSSepherosa Ziehau }
44876c1204dfSSepherosa Ziehau 
44886c1204dfSSepherosa Ziehau static int
44896c1204dfSSepherosa Ziehau hn_polling_sysctl(SYSCTL_HANDLER_ARGS)
44906c1204dfSSepherosa Ziehau {
44916c1204dfSSepherosa Ziehau 	struct hn_softc *sc = arg1;
44926c1204dfSSepherosa Ziehau 	int pollhz, error;
44936c1204dfSSepherosa Ziehau 
44946c1204dfSSepherosa Ziehau 	pollhz = sc->hn_pollhz;
44956c1204dfSSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pollhz, 0, req);
44966c1204dfSSepherosa Ziehau 	if (error || req->newptr == NULL)
44976c1204dfSSepherosa Ziehau 		return (error);
44986c1204dfSSepherosa Ziehau 
44996c1204dfSSepherosa Ziehau 	if (pollhz != 0 &&
45006c1204dfSSepherosa Ziehau 	    (pollhz < VMBUS_CHAN_POLLHZ_MIN || pollhz > VMBUS_CHAN_POLLHZ_MAX))
45016c1204dfSSepherosa Ziehau 		return (EINVAL);
45026c1204dfSSepherosa Ziehau 
45036c1204dfSSepherosa Ziehau 	HN_LOCK(sc);
45046c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz != pollhz) {
45056c1204dfSSepherosa Ziehau 		sc->hn_pollhz = pollhz;
45066c1204dfSSepherosa Ziehau 		if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) &&
45076c1204dfSSepherosa Ziehau 		    (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
45086c1204dfSSepherosa Ziehau 			hn_polling(sc, sc->hn_pollhz);
45096c1204dfSSepherosa Ziehau 	}
45106c1204dfSSepherosa Ziehau 	HN_UNLOCK(sc);
45116c1204dfSSepherosa Ziehau 
45126c1204dfSSepherosa Ziehau 	return (0);
45136c1204dfSSepherosa Ziehau }
45146c1204dfSSepherosa Ziehau 
4515dc13fee6SSepherosa Ziehau static int
451615516c77SSepherosa Ziehau hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS)
451715516c77SSepherosa Ziehau {
451815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
451915516c77SSepherosa Ziehau 	char verstr[16];
452015516c77SSepherosa Ziehau 
452115516c77SSepherosa Ziehau 	snprintf(verstr, sizeof(verstr), "%u.%u",
452215516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
452315516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
452415516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
452515516c77SSepherosa Ziehau }
452615516c77SSepherosa Ziehau 
452715516c77SSepherosa Ziehau static int
452815516c77SSepherosa Ziehau hn_caps_sysctl(SYSCTL_HANDLER_ARGS)
452915516c77SSepherosa Ziehau {
453015516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
453115516c77SSepherosa Ziehau 	char caps_str[128];
453215516c77SSepherosa Ziehau 	uint32_t caps;
453315516c77SSepherosa Ziehau 
453415516c77SSepherosa Ziehau 	HN_LOCK(sc);
453515516c77SSepherosa Ziehau 	caps = sc->hn_caps;
453615516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
453715516c77SSepherosa Ziehau 	snprintf(caps_str, sizeof(caps_str), "%b", caps, HN_CAP_BITS);
453815516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, caps_str, sizeof(caps_str), req);
453915516c77SSepherosa Ziehau }
454015516c77SSepherosa Ziehau 
454115516c77SSepherosa Ziehau static int
454215516c77SSepherosa Ziehau hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS)
454315516c77SSepherosa Ziehau {
454415516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
454515516c77SSepherosa Ziehau 	char assist_str[128];
454615516c77SSepherosa Ziehau 	uint32_t hwassist;
454715516c77SSepherosa Ziehau 
454815516c77SSepherosa Ziehau 	HN_LOCK(sc);
454915516c77SSepherosa Ziehau 	hwassist = sc->hn_ifp->if_hwassist;
455015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
455115516c77SSepherosa Ziehau 	snprintf(assist_str, sizeof(assist_str), "%b", hwassist, CSUM_BITS);
455215516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, assist_str, sizeof(assist_str), req);
455315516c77SSepherosa Ziehau }
455415516c77SSepherosa Ziehau 
455515516c77SSepherosa Ziehau static int
455615516c77SSepherosa Ziehau hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS)
455715516c77SSepherosa Ziehau {
455815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
455915516c77SSepherosa Ziehau 	char filter_str[128];
456015516c77SSepherosa Ziehau 	uint32_t filter;
456115516c77SSepherosa Ziehau 
456215516c77SSepherosa Ziehau 	HN_LOCK(sc);
456315516c77SSepherosa Ziehau 	filter = sc->hn_rx_filter;
456415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
456515516c77SSepherosa Ziehau 	snprintf(filter_str, sizeof(filter_str), "%b", filter,
456615516c77SSepherosa Ziehau 	    NDIS_PACKET_TYPES);
456715516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req);
456815516c77SSepherosa Ziehau }
456915516c77SSepherosa Ziehau 
457034d68912SSepherosa Ziehau #ifndef RSS
457134d68912SSepherosa Ziehau 
457215516c77SSepherosa Ziehau static int
457315516c77SSepherosa Ziehau hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS)
457415516c77SSepherosa Ziehau {
457515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
457615516c77SSepherosa Ziehau 	int error;
457715516c77SSepherosa Ziehau 
457815516c77SSepherosa Ziehau 	HN_LOCK(sc);
457915516c77SSepherosa Ziehau 
458015516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
458115516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
458215516c77SSepherosa Ziehau 		goto back;
458315516c77SSepherosa Ziehau 
4584642ec226SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) ||
4585642ec226SSepherosa Ziehau 	    (hn_xpnt_vf && sc->hn_vf_ifp != NULL)) {
4586642ec226SSepherosa Ziehau 		/*
4587642ec226SSepherosa Ziehau 		 * RSS key is synchronized w/ VF's, don't allow users
4588642ec226SSepherosa Ziehau 		 * to change it.
4589642ec226SSepherosa Ziehau 		 */
4590642ec226SSepherosa Ziehau 		error = EBUSY;
4591642ec226SSepherosa Ziehau 		goto back;
4592642ec226SSepherosa Ziehau 	}
4593642ec226SSepherosa Ziehau 
459415516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
459515516c77SSepherosa Ziehau 	if (error)
459615516c77SSepherosa Ziehau 		goto back;
459715516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
459815516c77SSepherosa Ziehau 
459915516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
460015516c77SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
460115516c77SSepherosa Ziehau 	} else {
460215516c77SSepherosa Ziehau 		/* Not RSS capable, at least for now; just save the RSS key. */
460315516c77SSepherosa Ziehau 		error = 0;
460415516c77SSepherosa Ziehau 	}
460515516c77SSepherosa Ziehau back:
460615516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
460715516c77SSepherosa Ziehau 	return (error);
460815516c77SSepherosa Ziehau }
460915516c77SSepherosa Ziehau 
461015516c77SSepherosa Ziehau static int
461115516c77SSepherosa Ziehau hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS)
461215516c77SSepherosa Ziehau {
461315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
461415516c77SSepherosa Ziehau 	int error;
461515516c77SSepherosa Ziehau 
461615516c77SSepherosa Ziehau 	HN_LOCK(sc);
461715516c77SSepherosa Ziehau 
461815516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
461915516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
462015516c77SSepherosa Ziehau 		goto back;
462115516c77SSepherosa Ziehau 
462215516c77SSepherosa Ziehau 	/*
462315516c77SSepherosa Ziehau 	 * Don't allow RSS indirect table change, if this interface is not
462415516c77SSepherosa Ziehau 	 * RSS capable currently.
462515516c77SSepherosa Ziehau 	 */
462615516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
462715516c77SSepherosa Ziehau 		error = EOPNOTSUPP;
462815516c77SSepherosa Ziehau 		goto back;
462915516c77SSepherosa Ziehau 	}
463015516c77SSepherosa Ziehau 
463115516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
463215516c77SSepherosa Ziehau 	if (error)
463315516c77SSepherosa Ziehau 		goto back;
463415516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSIND;
463515516c77SSepherosa Ziehau 
4636afd4971bSSepherosa Ziehau 	hn_rss_ind_fixup(sc);
463715516c77SSepherosa Ziehau 	error = hn_rss_reconfig(sc);
463815516c77SSepherosa Ziehau back:
463915516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
464015516c77SSepherosa Ziehau 	return (error);
464115516c77SSepherosa Ziehau }
464215516c77SSepherosa Ziehau 
464334d68912SSepherosa Ziehau #endif	/* !RSS */
464434d68912SSepherosa Ziehau 
464515516c77SSepherosa Ziehau static int
464615516c77SSepherosa Ziehau hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS)
464715516c77SSepherosa Ziehau {
464815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
464915516c77SSepherosa Ziehau 	char hash_str[128];
465015516c77SSepherosa Ziehau 	uint32_t hash;
465115516c77SSepherosa Ziehau 
465215516c77SSepherosa Ziehau 	HN_LOCK(sc);
465315516c77SSepherosa Ziehau 	hash = sc->hn_rss_hash;
465415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
465515516c77SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
465615516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
465715516c77SSepherosa Ziehau }
465815516c77SSepherosa Ziehau 
465915516c77SSepherosa Ziehau static int
4660642ec226SSepherosa Ziehau hn_rss_hcap_sysctl(SYSCTL_HANDLER_ARGS)
4661642ec226SSepherosa Ziehau {
4662642ec226SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4663642ec226SSepherosa Ziehau 	char hash_str[128];
4664642ec226SSepherosa Ziehau 	uint32_t hash;
4665642ec226SSepherosa Ziehau 
4666642ec226SSepherosa Ziehau 	HN_LOCK(sc);
4667642ec226SSepherosa Ziehau 	hash = sc->hn_rss_hcap;
4668642ec226SSepherosa Ziehau 	HN_UNLOCK(sc);
4669642ec226SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
4670642ec226SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
4671642ec226SSepherosa Ziehau }
4672642ec226SSepherosa Ziehau 
4673642ec226SSepherosa Ziehau static int
4674642ec226SSepherosa Ziehau hn_rss_mbuf_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_rx_ring[0].hn_mbuf_hash;
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
468840d60d6eSDexuan Cui hn_vf_sysctl(SYSCTL_HANDLER_ARGS)
468940d60d6eSDexuan Cui {
469040d60d6eSDexuan Cui 	struct hn_softc *sc = arg1;
4691499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
4692962f0357SSepherosa Ziehau 	struct ifnet *vf_ifp;
469340d60d6eSDexuan Cui 
469440d60d6eSDexuan Cui 	HN_LOCK(sc);
469540d60d6eSDexuan Cui 	vf_name[0] = '\0';
4696962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
4697962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
4698962f0357SSepherosa Ziehau 		snprintf(vf_name, sizeof(vf_name), "%s", vf_ifp->if_xname);
469940d60d6eSDexuan Cui 	HN_UNLOCK(sc);
470040d60d6eSDexuan Cui 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
470140d60d6eSDexuan Cui }
470240d60d6eSDexuan Cui 
470340d60d6eSDexuan Cui static int
4704499c3e17SSepherosa Ziehau hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS)
4705499c3e17SSepherosa Ziehau {
4706499c3e17SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4707499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
4708962f0357SSepherosa Ziehau 	struct ifnet *vf_ifp;
4709499c3e17SSepherosa Ziehau 
4710499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
4711499c3e17SSepherosa Ziehau 	vf_name[0] = '\0';
4712962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_rx_ring[0].hn_rxvf_ifp;
4713962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
4714962f0357SSepherosa Ziehau 		snprintf(vf_name, sizeof(vf_name), "%s", vf_ifp->if_xname);
4715499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
4716499c3e17SSepherosa Ziehau 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
4717499c3e17SSepherosa Ziehau }
4718499c3e17SSepherosa Ziehau 
4719499c3e17SSepherosa Ziehau static int
4720499c3e17SSepherosa Ziehau hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
4721499c3e17SSepherosa Ziehau {
4722499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4723499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4724499c3e17SSepherosa Ziehau 	int error, i;
4725499c3e17SSepherosa Ziehau 	bool first;
4726499c3e17SSepherosa Ziehau 
4727499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4728499c3e17SSepherosa Ziehau 	if (error != 0)
4729499c3e17SSepherosa Ziehau 		return (error);
4730499c3e17SSepherosa Ziehau 
4731499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4732499c3e17SSepherosa Ziehau 	if (sb == NULL)
4733499c3e17SSepherosa Ziehau 		return (ENOMEM);
4734499c3e17SSepherosa Ziehau 
4735499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4736499c3e17SSepherosa Ziehau 
4737499c3e17SSepherosa Ziehau 	first = true;
4738499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4739499c3e17SSepherosa Ziehau 		struct ifnet *ifp;
4740499c3e17SSepherosa Ziehau 
4741499c3e17SSepherosa Ziehau 		if (hn_vfmap[i] == NULL)
4742499c3e17SSepherosa Ziehau 			continue;
4743499c3e17SSepherosa Ziehau 
4744499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4745499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4746499c3e17SSepherosa Ziehau 			if (first)
4747499c3e17SSepherosa Ziehau 				sbuf_printf(sb, "%s", ifp->if_xname);
4748499c3e17SSepherosa Ziehau 			else
4749499c3e17SSepherosa Ziehau 				sbuf_printf(sb, " %s", ifp->if_xname);
4750499c3e17SSepherosa Ziehau 			first = false;
4751499c3e17SSepherosa Ziehau 		}
4752499c3e17SSepherosa Ziehau 	}
4753499c3e17SSepherosa Ziehau 
4754499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4755499c3e17SSepherosa Ziehau 
4756499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4757499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4758499c3e17SSepherosa Ziehau 	return (error);
4759499c3e17SSepherosa Ziehau }
4760499c3e17SSepherosa Ziehau 
4761499c3e17SSepherosa Ziehau static int
4762499c3e17SSepherosa Ziehau hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
4763499c3e17SSepherosa Ziehau {
4764499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4765499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4766499c3e17SSepherosa Ziehau 	int error, i;
4767499c3e17SSepherosa Ziehau 	bool first;
4768499c3e17SSepherosa Ziehau 
4769499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4770499c3e17SSepherosa Ziehau 	if (error != 0)
4771499c3e17SSepherosa Ziehau 		return (error);
4772499c3e17SSepherosa Ziehau 
4773499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4774499c3e17SSepherosa Ziehau 	if (sb == NULL)
4775499c3e17SSepherosa Ziehau 		return (ENOMEM);
4776499c3e17SSepherosa Ziehau 
4777499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4778499c3e17SSepherosa Ziehau 
4779499c3e17SSepherosa Ziehau 	first = true;
4780499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4781499c3e17SSepherosa Ziehau 		struct ifnet *ifp, *hn_ifp;
4782499c3e17SSepherosa Ziehau 
4783499c3e17SSepherosa Ziehau 		hn_ifp = hn_vfmap[i];
4784499c3e17SSepherosa Ziehau 		if (hn_ifp == NULL)
4785499c3e17SSepherosa Ziehau 			continue;
4786499c3e17SSepherosa Ziehau 
4787499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4788499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4789499c3e17SSepherosa Ziehau 			if (first) {
4790499c3e17SSepherosa Ziehau 				sbuf_printf(sb, "%s:%s", ifp->if_xname,
4791499c3e17SSepherosa Ziehau 				    hn_ifp->if_xname);
4792499c3e17SSepherosa Ziehau 			} else {
4793499c3e17SSepherosa Ziehau 				sbuf_printf(sb, " %s:%s", ifp->if_xname,
4794499c3e17SSepherosa Ziehau 				    hn_ifp->if_xname);
4795499c3e17SSepherosa Ziehau 			}
4796499c3e17SSepherosa Ziehau 			first = false;
4797499c3e17SSepherosa Ziehau 		}
4798499c3e17SSepherosa Ziehau 	}
4799499c3e17SSepherosa Ziehau 
4800499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4801499c3e17SSepherosa Ziehau 
4802499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4803499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4804499c3e17SSepherosa Ziehau 	return (error);
4805499c3e17SSepherosa Ziehau }
4806499c3e17SSepherosa Ziehau 
4807499c3e17SSepherosa Ziehau static int
48089c6cae24SSepherosa Ziehau hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS)
48099c6cae24SSepherosa Ziehau {
48109c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
48119c6cae24SSepherosa Ziehau 	int error, onoff = 0;
48129c6cae24SSepherosa Ziehau 
48139c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF)
48149c6cae24SSepherosa Ziehau 		onoff = 1;
48159c6cae24SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &onoff, 0, req);
48169c6cae24SSepherosa Ziehau 	if (error || req->newptr == NULL)
48179c6cae24SSepherosa Ziehau 		return (error);
48189c6cae24SSepherosa Ziehau 
48199c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
48209c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit() */
48219c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
48229c6cae24SSepherosa Ziehau 	if (onoff)
48239c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
48249c6cae24SSepherosa Ziehau 	else
48259c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags &= ~HN_XVFFLAG_ACCBPF;
48269c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
48279c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
48289c6cae24SSepherosa Ziehau 
48299c6cae24SSepherosa Ziehau 	return (0);
48309c6cae24SSepherosa Ziehau }
48319c6cae24SSepherosa Ziehau 
48329c6cae24SSepherosa Ziehau static int
48339c6cae24SSepherosa Ziehau hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS)
48349c6cae24SSepherosa Ziehau {
48359c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
48369c6cae24SSepherosa Ziehau 	int enabled = 0;
48379c6cae24SSepherosa Ziehau 
48389c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
48399c6cae24SSepherosa Ziehau 		enabled = 1;
48409c6cae24SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &enabled, 0, req));
48419c6cae24SSepherosa Ziehau }
48429c6cae24SSepherosa Ziehau 
48439c6cae24SSepherosa Ziehau static int
484415516c77SSepherosa Ziehau hn_check_iplen(const struct mbuf *m, int hoff)
484515516c77SSepherosa Ziehau {
484615516c77SSepherosa Ziehau 	const struct ip *ip;
484715516c77SSepherosa Ziehau 	int len, iphlen, iplen;
484815516c77SSepherosa Ziehau 	const struct tcphdr *th;
484915516c77SSepherosa Ziehau 	int thoff;				/* TCP data offset */
485015516c77SSepherosa Ziehau 
485115516c77SSepherosa Ziehau 	len = hoff + sizeof(struct ip);
485215516c77SSepherosa Ziehau 
485315516c77SSepherosa Ziehau 	/* The packet must be at least the size of an IP header. */
485415516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < len)
485515516c77SSepherosa Ziehau 		return IPPROTO_DONE;
485615516c77SSepherosa Ziehau 
485715516c77SSepherosa Ziehau 	/* The fixed IP header must reside completely in the first mbuf. */
485815516c77SSepherosa Ziehau 	if (m->m_len < len)
485915516c77SSepherosa Ziehau 		return IPPROTO_DONE;
486015516c77SSepherosa Ziehau 
486115516c77SSepherosa Ziehau 	ip = mtodo(m, hoff);
486215516c77SSepherosa Ziehau 
486315516c77SSepherosa Ziehau 	/* Bound check the packet's stated IP header length. */
486415516c77SSepherosa Ziehau 	iphlen = ip->ip_hl << 2;
486515516c77SSepherosa Ziehau 	if (iphlen < sizeof(struct ip))		/* minimum header length */
486615516c77SSepherosa Ziehau 		return IPPROTO_DONE;
486715516c77SSepherosa Ziehau 
486815516c77SSepherosa Ziehau 	/* The full IP header must reside completely in the one mbuf. */
486915516c77SSepherosa Ziehau 	if (m->m_len < hoff + iphlen)
487015516c77SSepherosa Ziehau 		return IPPROTO_DONE;
487115516c77SSepherosa Ziehau 
487215516c77SSepherosa Ziehau 	iplen = ntohs(ip->ip_len);
487315516c77SSepherosa Ziehau 
487415516c77SSepherosa Ziehau 	/*
487515516c77SSepherosa Ziehau 	 * Check that the amount of data in the buffers is as
487615516c77SSepherosa Ziehau 	 * at least much as the IP header would have us expect.
487715516c77SSepherosa Ziehau 	 */
487815516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < hoff + iplen)
487915516c77SSepherosa Ziehau 		return IPPROTO_DONE;
488015516c77SSepherosa Ziehau 
488115516c77SSepherosa Ziehau 	/*
488215516c77SSepherosa Ziehau 	 * Ignore IP fragments.
488315516c77SSepherosa Ziehau 	 */
488415516c77SSepherosa Ziehau 	if (ntohs(ip->ip_off) & (IP_OFFMASK | IP_MF))
488515516c77SSepherosa Ziehau 		return IPPROTO_DONE;
488615516c77SSepherosa Ziehau 
488715516c77SSepherosa Ziehau 	/*
488815516c77SSepherosa Ziehau 	 * The TCP/IP or UDP/IP header must be entirely contained within
488915516c77SSepherosa Ziehau 	 * the first fragment of a packet.
489015516c77SSepherosa Ziehau 	 */
489115516c77SSepherosa Ziehau 	switch (ip->ip_p) {
489215516c77SSepherosa Ziehau 	case IPPROTO_TCP:
489315516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct tcphdr))
489415516c77SSepherosa Ziehau 			return IPPROTO_DONE;
489515516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct tcphdr))
489615516c77SSepherosa Ziehau 			return IPPROTO_DONE;
489715516c77SSepherosa Ziehau 		th = (const struct tcphdr *)((const uint8_t *)ip + iphlen);
489815516c77SSepherosa Ziehau 		thoff = th->th_off << 2;
489915516c77SSepherosa Ziehau 		if (thoff < sizeof(struct tcphdr) || thoff + iphlen > iplen)
490015516c77SSepherosa Ziehau 			return IPPROTO_DONE;
490115516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + thoff)
490215516c77SSepherosa Ziehau 			return IPPROTO_DONE;
490315516c77SSepherosa Ziehau 		break;
490415516c77SSepherosa Ziehau 	case IPPROTO_UDP:
490515516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct udphdr))
490615516c77SSepherosa Ziehau 			return IPPROTO_DONE;
490715516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct udphdr))
490815516c77SSepherosa Ziehau 			return IPPROTO_DONE;
490915516c77SSepherosa Ziehau 		break;
491015516c77SSepherosa Ziehau 	default:
491115516c77SSepherosa Ziehau 		if (iplen < iphlen)
491215516c77SSepherosa Ziehau 			return IPPROTO_DONE;
491315516c77SSepherosa Ziehau 		break;
491415516c77SSepherosa Ziehau 	}
491515516c77SSepherosa Ziehau 	return ip->ip_p;
491615516c77SSepherosa Ziehau }
491715516c77SSepherosa Ziehau 
4918db76829bSSepherosa Ziehau static void
4919db76829bSSepherosa Ziehau hn_rxpkt_proto(const struct mbuf *m_new, int *l3proto, int *l4proto)
4920db76829bSSepherosa Ziehau {
4921db76829bSSepherosa Ziehau 	const struct ether_header *eh;
4922db76829bSSepherosa Ziehau 	uint16_t etype;
4923db76829bSSepherosa Ziehau 	int hoff;
4924db76829bSSepherosa Ziehau 
4925db76829bSSepherosa Ziehau 	hoff = sizeof(*eh);
4926db76829bSSepherosa Ziehau 	/* Checked at the beginning of this function. */
4927db76829bSSepherosa Ziehau 	KASSERT(m_new->m_len >= hoff, ("not ethernet frame"));
4928db76829bSSepherosa Ziehau 
4929db76829bSSepherosa Ziehau 	eh = mtod(m_new, const struct ether_header *);
4930db76829bSSepherosa Ziehau 	etype = ntohs(eh->ether_type);
4931db76829bSSepherosa Ziehau 	if (etype == ETHERTYPE_VLAN) {
4932db76829bSSepherosa Ziehau 		const struct ether_vlan_header *evl;
4933db76829bSSepherosa Ziehau 
4934db76829bSSepherosa Ziehau 		hoff = sizeof(*evl);
4935db76829bSSepherosa Ziehau 		if (m_new->m_len < hoff)
4936db76829bSSepherosa Ziehau 			return;
4937db76829bSSepherosa Ziehau 		evl = mtod(m_new, const struct ether_vlan_header *);
4938db76829bSSepherosa Ziehau 		etype = ntohs(evl->evl_proto);
4939db76829bSSepherosa Ziehau 	}
4940db76829bSSepherosa Ziehau 	*l3proto = etype;
4941db76829bSSepherosa Ziehau 
4942db76829bSSepherosa Ziehau 	if (etype == ETHERTYPE_IP)
4943db76829bSSepherosa Ziehau 		*l4proto = hn_check_iplen(m_new, hoff);
4944db76829bSSepherosa Ziehau 	else
4945db76829bSSepherosa Ziehau 		*l4proto = IPPROTO_DONE;
4946db76829bSSepherosa Ziehau }
4947db76829bSSepherosa Ziehau 
494815516c77SSepherosa Ziehau static int
494915516c77SSepherosa Ziehau hn_create_rx_data(struct hn_softc *sc, int ring_cnt)
495015516c77SSepherosa Ziehau {
495115516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
495215516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
495315516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
495415516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
495515516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
495615516c77SSepherosa Ziehau 	int lroent_cnt;
495715516c77SSepherosa Ziehau #endif
495815516c77SSepherosa Ziehau #endif
495915516c77SSepherosa Ziehau 	int i;
496015516c77SSepherosa Ziehau 
496115516c77SSepherosa Ziehau 	/*
496215516c77SSepherosa Ziehau 	 * Create RXBUF for reception.
496315516c77SSepherosa Ziehau 	 *
496415516c77SSepherosa Ziehau 	 * NOTE:
496515516c77SSepherosa Ziehau 	 * - It is shared by all channels.
496615516c77SSepherosa Ziehau 	 * - A large enough buffer is allocated, certain version of NVSes
496715516c77SSepherosa Ziehau 	 *   may further limit the usable space.
496815516c77SSepherosa Ziehau 	 */
496915516c77SSepherosa Ziehau 	sc->hn_rxbuf = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
497015516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_RXBUF_SIZE, &sc->hn_rxbuf_dma,
497115516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
497215516c77SSepherosa Ziehau 	if (sc->hn_rxbuf == NULL) {
497315516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate rxbuf failed\n");
497415516c77SSepherosa Ziehau 		return (ENOMEM);
497515516c77SSepherosa Ziehau 	}
497615516c77SSepherosa Ziehau 
497715516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = ring_cnt;
497815516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = sc->hn_rx_ring_cnt;
497915516c77SSepherosa Ziehau 
498015516c77SSepherosa Ziehau 	sc->hn_rx_ring = malloc(sizeof(struct hn_rx_ring) * sc->hn_rx_ring_cnt,
498115516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
498215516c77SSepherosa Ziehau 
498315516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
498415516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
498515516c77SSepherosa Ziehau 	lroent_cnt = hn_lro_entry_count;
498615516c77SSepherosa Ziehau 	if (lroent_cnt < TCP_LRO_ENTRIES)
498715516c77SSepherosa Ziehau 		lroent_cnt = TCP_LRO_ENTRIES;
498815516c77SSepherosa Ziehau 	if (bootverbose)
498915516c77SSepherosa Ziehau 		device_printf(dev, "LRO: entry count %d\n", lroent_cnt);
499015516c77SSepherosa Ziehau #endif
499115516c77SSepherosa Ziehau #endif	/* INET || INET6 */
499215516c77SSepherosa Ziehau 
499315516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
499415516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
499515516c77SSepherosa Ziehau 
499615516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.rx sysctl tree */
499715516c77SSepherosa Ziehau 	sc->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rx",
499815516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
499915516c77SSepherosa Ziehau 
500015516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
500115516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
500215516c77SSepherosa Ziehau 
500315516c77SSepherosa Ziehau 		rxr->hn_br = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
500415516c77SSepherosa Ziehau 		    PAGE_SIZE, 0, HN_TXBR_SIZE + HN_RXBR_SIZE,
500515516c77SSepherosa Ziehau 		    &rxr->hn_br_dma, BUS_DMA_WAITOK);
500615516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL) {
500715516c77SSepherosa Ziehau 			device_printf(dev, "allocate bufring failed\n");
500815516c77SSepherosa Ziehau 			return (ENOMEM);
500915516c77SSepherosa Ziehau 		}
501015516c77SSepherosa Ziehau 
501115516c77SSepherosa Ziehau 		if (hn_trust_hosttcp)
501215516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_TCP;
501315516c77SSepherosa Ziehau 		if (hn_trust_hostudp)
501415516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_UDP;
501515516c77SSepherosa Ziehau 		if (hn_trust_hostip)
501615516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_IP;
5017642ec226SSepherosa Ziehau 		rxr->hn_mbuf_hash = NDIS_HASH_ALL;
501815516c77SSepherosa Ziehau 		rxr->hn_ifp = sc->hn_ifp;
501915516c77SSepherosa Ziehau 		if (i < sc->hn_tx_ring_cnt)
502015516c77SSepherosa Ziehau 			rxr->hn_txr = &sc->hn_tx_ring[i];
502115516c77SSepherosa Ziehau 		rxr->hn_pktbuf_len = HN_PKTBUF_LEN_DEF;
502215516c77SSepherosa Ziehau 		rxr->hn_pktbuf = malloc(rxr->hn_pktbuf_len, M_DEVBUF, M_WAITOK);
502315516c77SSepherosa Ziehau 		rxr->hn_rx_idx = i;
502415516c77SSepherosa Ziehau 		rxr->hn_rxbuf = sc->hn_rxbuf;
502515516c77SSepherosa Ziehau 
502615516c77SSepherosa Ziehau 		/*
502715516c77SSepherosa Ziehau 		 * Initialize LRO.
502815516c77SSepherosa Ziehau 		 */
502915516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
503015516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
503115516c77SSepherosa Ziehau 		tcp_lro_init_args(&rxr->hn_lro, sc->hn_ifp, lroent_cnt,
503215516c77SSepherosa Ziehau 		    hn_lro_mbufq_depth);
503315516c77SSepherosa Ziehau #else
503415516c77SSepherosa Ziehau 		tcp_lro_init(&rxr->hn_lro);
503515516c77SSepherosa Ziehau 		rxr->hn_lro.ifp = sc->hn_ifp;
503615516c77SSepherosa Ziehau #endif
503715516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
503815516c77SSepherosa Ziehau 		rxr->hn_lro.lro_length_lim = HN_LRO_LENLIM_DEF;
503915516c77SSepherosa Ziehau 		rxr->hn_lro.lro_ackcnt_lim = HN_LRO_ACKCNT_DEF;
504015516c77SSepherosa Ziehau #endif
504115516c77SSepherosa Ziehau #endif	/* INET || INET6 */
504215516c77SSepherosa Ziehau 
504315516c77SSepherosa Ziehau 		if (sc->hn_rx_sysctl_tree != NULL) {
504415516c77SSepherosa Ziehau 			char name[16];
504515516c77SSepherosa Ziehau 
504615516c77SSepherosa Ziehau 			/*
504715516c77SSepherosa Ziehau 			 * Create per RX ring sysctl tree:
504815516c77SSepherosa Ziehau 			 * dev.hn.UNIT.rx.RINGID
504915516c77SSepherosa Ziehau 			 */
505015516c77SSepherosa Ziehau 			snprintf(name, sizeof(name), "%d", i);
505115516c77SSepherosa Ziehau 			rxr->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx,
505215516c77SSepherosa Ziehau 			    SYSCTL_CHILDREN(sc->hn_rx_sysctl_tree),
505315516c77SSepherosa Ziehau 			    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
505415516c77SSepherosa Ziehau 
505515516c77SSepherosa Ziehau 			if (rxr->hn_rx_sysctl_tree != NULL) {
505615516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
505715516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
505815516c77SSepherosa Ziehau 				    OID_AUTO, "packets", CTLFLAG_RW,
505915516c77SSepherosa Ziehau 				    &rxr->hn_pkts, "# of packets received");
506015516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
506115516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
506215516c77SSepherosa Ziehau 				    OID_AUTO, "rss_pkts", CTLFLAG_RW,
506315516c77SSepherosa Ziehau 				    &rxr->hn_rss_pkts,
506415516c77SSepherosa Ziehau 				    "# of packets w/ RSS info received");
5065a491581fSWei Hu 				SYSCTL_ADD_ULONG(ctx,
5066a491581fSWei Hu 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5067a491581fSWei Hu 				    OID_AUTO, "rsc_pkts", CTLFLAG_RW,
5068a491581fSWei Hu 				    &rxr->hn_rsc_pkts,
5069a491581fSWei Hu 				    "# of RSC packets received");
5070a491581fSWei Hu 				SYSCTL_ADD_ULONG(ctx,
5071a491581fSWei Hu 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
5072a491581fSWei Hu 				    OID_AUTO, "rsc_drop", CTLFLAG_RW,
5073a491581fSWei Hu 				    &rxr->hn_rsc_drop,
5074a491581fSWei Hu 				    "# of RSC fragments dropped");
507515516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx,
507615516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
507715516c77SSepherosa Ziehau 				    OID_AUTO, "pktbuf_len", CTLFLAG_RD,
507815516c77SSepherosa Ziehau 				    &rxr->hn_pktbuf_len, 0,
507915516c77SSepherosa Ziehau 				    "Temporary channel packet buffer length");
508015516c77SSepherosa Ziehau 			}
508115516c77SSepherosa Ziehau 		}
508215516c77SSepherosa Ziehau 	}
508315516c77SSepherosa Ziehau 
508415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_queued",
508515516c77SSepherosa Ziehau 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
508615516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_queued),
508715516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
508815516c77SSepherosa Ziehau 	    hn_rx_stat_int_sysctl,
508915516c77SSepherosa Ziehau #else
509015516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
509115516c77SSepherosa Ziehau #endif
509215516c77SSepherosa Ziehau 	    "LU", "LRO queued");
509315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_flushed",
509415516c77SSepherosa Ziehau 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
509515516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_flushed),
509615516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
509715516c77SSepherosa Ziehau 	    hn_rx_stat_int_sysctl,
509815516c77SSepherosa Ziehau #else
509915516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
510015516c77SSepherosa Ziehau #endif
510115516c77SSepherosa Ziehau 	    "LU", "LRO flushed");
510215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_tried",
510315516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
510415516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro_tried),
510515516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of LRO tries");
510615516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
510715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_length_lim",
510815516c77SSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
510915516c77SSepherosa Ziehau 	    hn_lro_lenlim_sysctl, "IU",
511015516c77SSepherosa Ziehau 	    "Max # of data bytes to be aggregated by LRO");
511115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_ackcnt_lim",
511215516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
511315516c77SSepherosa Ziehau 	    hn_lro_ackcnt_sysctl, "I",
511415516c77SSepherosa Ziehau 	    "Max # of ACKs to be aggregated by LRO");
511515516c77SSepherosa Ziehau #endif
511615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hosttcp",
511715516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_TCP,
511815516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
5119*b15a632cSGordon Bergling 	    "Trust tcp segment verification on host side, "
512015516c77SSepherosa Ziehau 	    "when csum info is missing");
512115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostudp",
512215516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_UDP,
512315516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
512415516c77SSepherosa Ziehau 	    "Trust udp datagram verification on host side, "
512515516c77SSepherosa Ziehau 	    "when csum info is missing");
512615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostip",
512715516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_IP,
512815516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
512915516c77SSepherosa Ziehau 	    "Trust ip packet verification on host side, "
513015516c77SSepherosa Ziehau 	    "when csum info is missing");
513115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_ip",
513215516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
513315516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_ip),
513415516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM IP");
513515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_tcp",
513615516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
513715516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_tcp),
513815516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM TCP");
513915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_udp",
514015516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
514115516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_udp),
514215516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM UDP");
514315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_trusted",
514415516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
514515516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_trusted),
514615516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU",
514715516c77SSepherosa Ziehau 	    "# of packets that we trust host's csum verification");
514815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "small_pkts",
514915516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
515015516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_small_pkts),
515115516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of small packets received");
515215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_ack_failed",
515315516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
515415516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_ack_failed),
515515516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of RXBUF ack failures");
515615516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_cnt",
515715516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_cnt, 0, "# created RX rings");
515815516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_inuse",
515915516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_inuse, 0, "# used RX rings");
516015516c77SSepherosa Ziehau 
516115516c77SSepherosa Ziehau 	return (0);
516215516c77SSepherosa Ziehau }
516315516c77SSepherosa Ziehau 
516415516c77SSepherosa Ziehau static void
516515516c77SSepherosa Ziehau hn_destroy_rx_data(struct hn_softc *sc)
516615516c77SSepherosa Ziehau {
516715516c77SSepherosa Ziehau 	int i;
516815516c77SSepherosa Ziehau 
516915516c77SSepherosa Ziehau 	if (sc->hn_rxbuf != NULL) {
51702494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXBUF_REF) == 0)
517115516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_rxbuf_dma, sc->hn_rxbuf);
51722494d735SSepherosa Ziehau 		else
51732494d735SSepherosa Ziehau 			device_printf(sc->hn_dev, "RXBUF is referenced\n");
517415516c77SSepherosa Ziehau 		sc->hn_rxbuf = NULL;
517515516c77SSepherosa Ziehau 	}
517615516c77SSepherosa Ziehau 
517715516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_cnt == 0)
517815516c77SSepherosa Ziehau 		return;
517915516c77SSepherosa Ziehau 
518015516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
518115516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
518215516c77SSepherosa Ziehau 
518315516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL)
518415516c77SSepherosa Ziehau 			continue;
51852494d735SSepherosa Ziehau 		if ((rxr->hn_rx_flags & HN_RX_FLAG_BR_REF) == 0) {
518615516c77SSepherosa Ziehau 			hyperv_dmamem_free(&rxr->hn_br_dma, rxr->hn_br);
51872494d735SSepherosa Ziehau 		} else {
51882494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
51892494d735SSepherosa Ziehau 			    "%dth channel bufring is referenced", i);
51902494d735SSepherosa Ziehau 		}
519115516c77SSepherosa Ziehau 		rxr->hn_br = NULL;
519215516c77SSepherosa Ziehau 
519315516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
519415516c77SSepherosa Ziehau 		tcp_lro_free(&rxr->hn_lro);
519515516c77SSepherosa Ziehau #endif
519615516c77SSepherosa Ziehau 		free(rxr->hn_pktbuf, M_DEVBUF);
519715516c77SSepherosa Ziehau 	}
519815516c77SSepherosa Ziehau 	free(sc->hn_rx_ring, M_DEVBUF);
519915516c77SSepherosa Ziehau 	sc->hn_rx_ring = NULL;
520015516c77SSepherosa Ziehau 
520115516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = 0;
520215516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = 0;
520315516c77SSepherosa Ziehau }
520415516c77SSepherosa Ziehau 
520515516c77SSepherosa Ziehau static int
520615516c77SSepherosa Ziehau hn_tx_ring_create(struct hn_softc *sc, int id)
520715516c77SSepherosa Ziehau {
520815516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[id];
520915516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
521015516c77SSepherosa Ziehau 	bus_dma_tag_t parent_dtag;
521115516c77SSepherosa Ziehau 	int error, i;
521215516c77SSepherosa Ziehau 
521315516c77SSepherosa Ziehau 	txr->hn_sc = sc;
521415516c77SSepherosa Ziehau 	txr->hn_tx_idx = id;
521515516c77SSepherosa Ziehau 
521615516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
521715516c77SSepherosa Ziehau 	mtx_init(&txr->hn_txlist_spin, "hn txlist", NULL, MTX_SPIN);
521815516c77SSepherosa Ziehau #endif
521915516c77SSepherosa Ziehau 	mtx_init(&txr->hn_tx_lock, "hn tx", NULL, MTX_DEF);
522015516c77SSepherosa Ziehau 
522115516c77SSepherosa Ziehau 	txr->hn_txdesc_cnt = HN_TX_DESC_CNT;
522215516c77SSepherosa Ziehau 	txr->hn_txdesc = malloc(sizeof(struct hn_txdesc) * txr->hn_txdesc_cnt,
522315516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
522415516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
522515516c77SSepherosa Ziehau 	SLIST_INIT(&txr->hn_txlist);
522615516c77SSepherosa Ziehau #else
522715516c77SSepherosa Ziehau 	txr->hn_txdesc_br = buf_ring_alloc(txr->hn_txdesc_cnt, M_DEVBUF,
522815516c77SSepherosa Ziehau 	    M_WAITOK, &txr->hn_tx_lock);
522915516c77SSepherosa Ziehau #endif
523015516c77SSepherosa Ziehau 
52310e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_EVTTQ) {
52320e11868dSSepherosa Ziehau 		txr->hn_tx_taskq = VMBUS_GET_EVENT_TASKQ(
52330e11868dSSepherosa Ziehau 		    device_get_parent(dev), dev, HN_RING_IDX2CPU(sc, id));
52340e11868dSSepherosa Ziehau 	} else {
5235fdd0222aSSepherosa Ziehau 		txr->hn_tx_taskq = sc->hn_tx_taskqs[id % hn_tx_taskq_cnt];
52360e11868dSSepherosa Ziehau 	}
523715516c77SSepherosa Ziehau 
523823bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
523915516c77SSepherosa Ziehau 	if (hn_use_if_start) {
524015516c77SSepherosa Ziehau 		txr->hn_txeof = hn_start_txeof;
524115516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_start_taskfunc, txr);
524215516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_start_txeof_taskfunc, txr);
524323bf9e15SSepherosa Ziehau 	} else
524423bf9e15SSepherosa Ziehau #endif
524523bf9e15SSepherosa Ziehau 	{
524615516c77SSepherosa Ziehau 		int br_depth;
524715516c77SSepherosa Ziehau 
524815516c77SSepherosa Ziehau 		txr->hn_txeof = hn_xmit_txeof;
524915516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_xmit_taskfunc, txr);
525015516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_xmit_txeof_taskfunc, txr);
525115516c77SSepherosa Ziehau 
525215516c77SSepherosa Ziehau 		br_depth = hn_get_txswq_depth(txr);
525315516c77SSepherosa Ziehau 		txr->hn_mbuf_br = buf_ring_alloc(br_depth, M_DEVBUF,
525415516c77SSepherosa Ziehau 		    M_WAITOK, &txr->hn_tx_lock);
525515516c77SSepherosa Ziehau 	}
525615516c77SSepherosa Ziehau 
525715516c77SSepherosa Ziehau 	txr->hn_direct_tx_size = hn_direct_tx_size;
525815516c77SSepherosa Ziehau 
525915516c77SSepherosa Ziehau 	/*
526015516c77SSepherosa Ziehau 	 * Always schedule transmission instead of trying to do direct
526115516c77SSepherosa Ziehau 	 * transmission.  This one gives the best performance so far.
526215516c77SSepherosa Ziehau 	 */
526315516c77SSepherosa Ziehau 	txr->hn_sched_tx = 1;
526415516c77SSepherosa Ziehau 
526515516c77SSepherosa Ziehau 	parent_dtag = bus_get_dma_tag(dev);
526615516c77SSepherosa Ziehau 
526715516c77SSepherosa Ziehau 	/* DMA tag for RNDIS packet messages. */
526815516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
526915516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_ALIGN,		/* alignment */
527015516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_BOUNDARY,	/* boundary */
527115516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
527215516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
527315516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
527415516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsize */
527515516c77SSepherosa Ziehau 	    1,				/* nsegments */
527615516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsegsize */
527715516c77SSepherosa Ziehau 	    0,				/* flags */
527815516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
527915516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
528015516c77SSepherosa Ziehau 	    &txr->hn_tx_rndis_dtag);
528115516c77SSepherosa Ziehau 	if (error) {
528215516c77SSepherosa Ziehau 		device_printf(dev, "failed to create rndis dmatag\n");
528315516c77SSepherosa Ziehau 		return error;
528415516c77SSepherosa Ziehau 	}
528515516c77SSepherosa Ziehau 
528615516c77SSepherosa Ziehau 	/* DMA tag for data. */
528715516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
528815516c77SSepherosa Ziehau 	    1,				/* alignment */
528915516c77SSepherosa Ziehau 	    HN_TX_DATA_BOUNDARY,	/* boundary */
529015516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
529115516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
529215516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
529315516c77SSepherosa Ziehau 	    HN_TX_DATA_MAXSIZE,		/* maxsize */
529415516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGCNT_MAX,	/* nsegments */
529515516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGSIZE,		/* maxsegsize */
529615516c77SSepherosa Ziehau 	    0,				/* flags */
529715516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
529815516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
529915516c77SSepherosa Ziehau 	    &txr->hn_tx_data_dtag);
530015516c77SSepherosa Ziehau 	if (error) {
530115516c77SSepherosa Ziehau 		device_printf(dev, "failed to create data dmatag\n");
530215516c77SSepherosa Ziehau 		return error;
530315516c77SSepherosa Ziehau 	}
530415516c77SSepherosa Ziehau 
530515516c77SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i) {
530615516c77SSepherosa Ziehau 		struct hn_txdesc *txd = &txr->hn_txdesc[i];
530715516c77SSepherosa Ziehau 
530815516c77SSepherosa Ziehau 		txd->txr = txr;
530915516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
5310dc13fee6SSepherosa Ziehau 		STAILQ_INIT(&txd->agg_list);
531115516c77SSepherosa Ziehau 
531215516c77SSepherosa Ziehau 		/*
531315516c77SSepherosa Ziehau 		 * Allocate and load RNDIS packet message.
531415516c77SSepherosa Ziehau 		 */
531515516c77SSepherosa Ziehau         	error = bus_dmamem_alloc(txr->hn_tx_rndis_dtag,
531615516c77SSepherosa Ziehau 		    (void **)&txd->rndis_pkt,
531715516c77SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
531815516c77SSepherosa Ziehau 		    &txd->rndis_pkt_dmap);
531915516c77SSepherosa Ziehau 		if (error) {
532015516c77SSepherosa Ziehau 			device_printf(dev,
532115516c77SSepherosa Ziehau 			    "failed to allocate rndis_packet_msg, %d\n", i);
532215516c77SSepherosa Ziehau 			return error;
532315516c77SSepherosa Ziehau 		}
532415516c77SSepherosa Ziehau 
532515516c77SSepherosa Ziehau 		error = bus_dmamap_load(txr->hn_tx_rndis_dtag,
532615516c77SSepherosa Ziehau 		    txd->rndis_pkt_dmap,
532715516c77SSepherosa Ziehau 		    txd->rndis_pkt, HN_RNDIS_PKT_LEN,
532815516c77SSepherosa Ziehau 		    hyperv_dma_map_paddr, &txd->rndis_pkt_paddr,
532915516c77SSepherosa Ziehau 		    BUS_DMA_NOWAIT);
533015516c77SSepherosa Ziehau 		if (error) {
533115516c77SSepherosa Ziehau 			device_printf(dev,
533215516c77SSepherosa Ziehau 			    "failed to load rndis_packet_msg, %d\n", i);
533315516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
533415516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
533515516c77SSepherosa Ziehau 			return error;
533615516c77SSepherosa Ziehau 		}
533715516c77SSepherosa Ziehau 
533815516c77SSepherosa Ziehau 		/* DMA map for TX data. */
533915516c77SSepherosa Ziehau 		error = bus_dmamap_create(txr->hn_tx_data_dtag, 0,
534015516c77SSepherosa Ziehau 		    &txd->data_dmap);
534115516c77SSepherosa Ziehau 		if (error) {
534215516c77SSepherosa Ziehau 			device_printf(dev,
534315516c77SSepherosa Ziehau 			    "failed to allocate tx data dmamap\n");
534415516c77SSepherosa Ziehau 			bus_dmamap_unload(txr->hn_tx_rndis_dtag,
534515516c77SSepherosa Ziehau 			    txd->rndis_pkt_dmap);
534615516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
534715516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
534815516c77SSepherosa Ziehau 			return error;
534915516c77SSepherosa Ziehau 		}
535015516c77SSepherosa Ziehau 
535115516c77SSepherosa Ziehau 		/* All set, put it to list */
535215516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_ONLIST;
535315516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
535415516c77SSepherosa Ziehau 		SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
535515516c77SSepherosa Ziehau #else
535615516c77SSepherosa Ziehau 		buf_ring_enqueue(txr->hn_txdesc_br, txd);
535715516c77SSepherosa Ziehau #endif
535815516c77SSepherosa Ziehau 	}
535915516c77SSepherosa Ziehau 	txr->hn_txdesc_avail = txr->hn_txdesc_cnt;
536015516c77SSepherosa Ziehau 
536115516c77SSepherosa Ziehau 	if (sc->hn_tx_sysctl_tree != NULL) {
536215516c77SSepherosa Ziehau 		struct sysctl_oid_list *child;
536315516c77SSepherosa Ziehau 		struct sysctl_ctx_list *ctx;
536415516c77SSepherosa Ziehau 		char name[16];
536515516c77SSepherosa Ziehau 
536615516c77SSepherosa Ziehau 		/*
536715516c77SSepherosa Ziehau 		 * Create per TX ring sysctl tree:
536815516c77SSepherosa Ziehau 		 * dev.hn.UNIT.tx.RINGID
536915516c77SSepherosa Ziehau 		 */
537015516c77SSepherosa Ziehau 		ctx = device_get_sysctl_ctx(dev);
537115516c77SSepherosa Ziehau 		child = SYSCTL_CHILDREN(sc->hn_tx_sysctl_tree);
537215516c77SSepherosa Ziehau 
537315516c77SSepherosa Ziehau 		snprintf(name, sizeof(name), "%d", id);
537415516c77SSepherosa Ziehau 		txr->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
537515516c77SSepherosa Ziehau 		    name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
537615516c77SSepherosa Ziehau 
537715516c77SSepherosa Ziehau 		if (txr->hn_tx_sysctl_tree != NULL) {
537815516c77SSepherosa Ziehau 			child = SYSCTL_CHILDREN(txr->hn_tx_sysctl_tree);
537915516c77SSepherosa Ziehau 
538085e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
538115516c77SSepherosa Ziehau 			SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_avail",
538215516c77SSepherosa Ziehau 			    CTLFLAG_RD, &txr->hn_txdesc_avail, 0,
538315516c77SSepherosa Ziehau 			    "# of available TX descs");
538485e4ae1eSSepherosa Ziehau #endif
538523bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
538623bf9e15SSepherosa Ziehau 			if (!hn_use_if_start)
538723bf9e15SSepherosa Ziehau #endif
538823bf9e15SSepherosa Ziehau 			{
538915516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx, child, OID_AUTO, "oactive",
539015516c77SSepherosa Ziehau 				    CTLFLAG_RD, &txr->hn_oactive, 0,
539115516c77SSepherosa Ziehau 				    "over active");
539215516c77SSepherosa Ziehau 			}
539315516c77SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "packets",
539415516c77SSepherosa Ziehau 			    CTLFLAG_RW, &txr->hn_pkts,
539515516c77SSepherosa Ziehau 			    "# of packets transmitted");
5396dc13fee6SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "sends",
5397dc13fee6SSepherosa Ziehau 			    CTLFLAG_RW, &txr->hn_sends, "# of sends");
539815516c77SSepherosa Ziehau 		}
539915516c77SSepherosa Ziehau 	}
540015516c77SSepherosa Ziehau 
540115516c77SSepherosa Ziehau 	return 0;
540215516c77SSepherosa Ziehau }
540315516c77SSepherosa Ziehau 
540415516c77SSepherosa Ziehau static void
540515516c77SSepherosa Ziehau hn_txdesc_dmamap_destroy(struct hn_txdesc *txd)
540615516c77SSepherosa Ziehau {
540715516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = txd->txr;
540815516c77SSepherosa Ziehau 
540915516c77SSepherosa Ziehau 	KASSERT(txd->m == NULL, ("still has mbuf installed"));
541015516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("still dma mapped"));
541115516c77SSepherosa Ziehau 
541215516c77SSepherosa Ziehau 	bus_dmamap_unload(txr->hn_tx_rndis_dtag, txd->rndis_pkt_dmap);
541315516c77SSepherosa Ziehau 	bus_dmamem_free(txr->hn_tx_rndis_dtag, txd->rndis_pkt,
541415516c77SSepherosa Ziehau 	    txd->rndis_pkt_dmap);
541515516c77SSepherosa Ziehau 	bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap);
541615516c77SSepherosa Ziehau }
541715516c77SSepherosa Ziehau 
541815516c77SSepherosa Ziehau static void
541925641fc7SSepherosa Ziehau hn_txdesc_gc(struct hn_tx_ring *txr, struct hn_txdesc *txd)
542025641fc7SSepherosa Ziehau {
542125641fc7SSepherosa Ziehau 
542225641fc7SSepherosa Ziehau 	KASSERT(txd->refs == 0 || txd->refs == 1,
542325641fc7SSepherosa Ziehau 	    ("invalid txd refs %d", txd->refs));
542425641fc7SSepherosa Ziehau 
542525641fc7SSepherosa Ziehau 	/* Aggregated txds will be freed by their aggregating txd. */
542625641fc7SSepherosa Ziehau 	if (txd->refs > 0 && (txd->flags & HN_TXD_FLAG_ONAGG) == 0) {
542725641fc7SSepherosa Ziehau 		int freed;
542825641fc7SSepherosa Ziehau 
542925641fc7SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
543025641fc7SSepherosa Ziehau 		KASSERT(freed, ("can't free txdesc"));
543125641fc7SSepherosa Ziehau 	}
543225641fc7SSepherosa Ziehau }
543325641fc7SSepherosa Ziehau 
543425641fc7SSepherosa Ziehau static void
543515516c77SSepherosa Ziehau hn_tx_ring_destroy(struct hn_tx_ring *txr)
543615516c77SSepherosa Ziehau {
543725641fc7SSepherosa Ziehau 	int i;
543815516c77SSepherosa Ziehau 
543915516c77SSepherosa Ziehau 	if (txr->hn_txdesc == NULL)
544015516c77SSepherosa Ziehau 		return;
544115516c77SSepherosa Ziehau 
544225641fc7SSepherosa Ziehau 	/*
544325641fc7SSepherosa Ziehau 	 * NOTE:
544425641fc7SSepherosa Ziehau 	 * Because the freeing of aggregated txds will be deferred
544525641fc7SSepherosa Ziehau 	 * to the aggregating txd, two passes are used here:
544625641fc7SSepherosa Ziehau 	 * - The first pass GCes any pending txds.  This GC is necessary,
544725641fc7SSepherosa Ziehau 	 *   since if the channels are revoked, hypervisor will not
544825641fc7SSepherosa Ziehau 	 *   deliver send-done for all pending txds.
544925641fc7SSepherosa Ziehau 	 * - The second pass frees the busdma stuffs, i.e. after all txds
545025641fc7SSepherosa Ziehau 	 *   were freed.
545125641fc7SSepherosa Ziehau 	 */
545225641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
545325641fc7SSepherosa Ziehau 		hn_txdesc_gc(txr, &txr->hn_txdesc[i]);
545425641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
545525641fc7SSepherosa Ziehau 		hn_txdesc_dmamap_destroy(&txr->hn_txdesc[i]);
545615516c77SSepherosa Ziehau 
545715516c77SSepherosa Ziehau 	if (txr->hn_tx_data_dtag != NULL)
545815516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_data_dtag);
545915516c77SSepherosa Ziehau 	if (txr->hn_tx_rndis_dtag != NULL)
546015516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_rndis_dtag);
546115516c77SSepherosa Ziehau 
546215516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
546315516c77SSepherosa Ziehau 	buf_ring_free(txr->hn_txdesc_br, M_DEVBUF);
546415516c77SSepherosa Ziehau #endif
546515516c77SSepherosa Ziehau 
546615516c77SSepherosa Ziehau 	free(txr->hn_txdesc, M_DEVBUF);
546715516c77SSepherosa Ziehau 	txr->hn_txdesc = NULL;
546815516c77SSepherosa Ziehau 
546915516c77SSepherosa Ziehau 	if (txr->hn_mbuf_br != NULL)
547015516c77SSepherosa Ziehau 		buf_ring_free(txr->hn_mbuf_br, M_DEVBUF);
547115516c77SSepherosa Ziehau 
547215516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
547315516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_txlist_spin);
547415516c77SSepherosa Ziehau #endif
547515516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_tx_lock);
547615516c77SSepherosa Ziehau }
547715516c77SSepherosa Ziehau 
547815516c77SSepherosa Ziehau static int
547915516c77SSepherosa Ziehau hn_create_tx_data(struct hn_softc *sc, int ring_cnt)
548015516c77SSepherosa Ziehau {
548115516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
548215516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
548315516c77SSepherosa Ziehau 	int i;
548415516c77SSepherosa Ziehau 
548515516c77SSepherosa Ziehau 	/*
548615516c77SSepherosa Ziehau 	 * Create TXBUF for chimney sending.
548715516c77SSepherosa Ziehau 	 *
548815516c77SSepherosa Ziehau 	 * NOTE: It is shared by all channels.
548915516c77SSepherosa Ziehau 	 */
549015516c77SSepherosa Ziehau 	sc->hn_chim = hyperv_dmamem_alloc(bus_get_dma_tag(sc->hn_dev),
549115516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_CHIM_SIZE, &sc->hn_chim_dma,
549215516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
549315516c77SSepherosa Ziehau 	if (sc->hn_chim == NULL) {
549415516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate txbuf failed\n");
549515516c77SSepherosa Ziehau 		return (ENOMEM);
549615516c77SSepherosa Ziehau 	}
549715516c77SSepherosa Ziehau 
549815516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = ring_cnt;
549915516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
550015516c77SSepherosa Ziehau 
550115516c77SSepherosa Ziehau 	sc->hn_tx_ring = malloc(sizeof(struct hn_tx_ring) * sc->hn_tx_ring_cnt,
550215516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
550315516c77SSepherosa Ziehau 
550415516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(sc->hn_dev);
550515516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->hn_dev));
550615516c77SSepherosa Ziehau 
550715516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.tx sysctl tree */
550815516c77SSepherosa Ziehau 	sc->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tx",
550915516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
551015516c77SSepherosa Ziehau 
551115516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
551215516c77SSepherosa Ziehau 		int error;
551315516c77SSepherosa Ziehau 
551415516c77SSepherosa Ziehau 		error = hn_tx_ring_create(sc, i);
551515516c77SSepherosa Ziehau 		if (error)
551615516c77SSepherosa Ziehau 			return error;
551715516c77SSepherosa Ziehau 	}
551815516c77SSepherosa Ziehau 
551915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "no_txdescs",
552015516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
552115516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_no_txdescs),
552215516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of times short of TX descs");
552315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "send_failed",
552415516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
552515516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_send_failed),
552615516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of hyper-v sending failure");
552715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdma_failed",
552815516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
552915516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_txdma_failed),
553015516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX DMA failure");
5531dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_flush_failed",
5532dc13fee6SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
5533dc13fee6SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_flush_failed),
5534dc13fee6SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU",
5535dc13fee6SSepherosa Ziehau 	    "# of packet transmission aggregation flush failure");
553615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_collapsed",
553715516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
553815516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_collapsed),
553915516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX mbuf collapsed");
554015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney",
554115516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
554215516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney),
554315516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send");
554415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_tried",
554515516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
554615516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney_tried),
554715516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send tries");
554815516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_cnt",
554915516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_txdesc_cnt, 0,
555015516c77SSepherosa Ziehau 	    "# of total TX descs");
555115516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_chimney_max",
555215516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_chim_szmax, 0,
555315516c77SSepherosa Ziehau 	    "Chimney send packet size upper boundary");
555415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_size",
555515516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
555615516c77SSepherosa Ziehau 	    hn_chim_size_sysctl, "I", "Chimney send packet size limit");
555715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "direct_tx_size",
555815516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
555915516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_direct_tx_size),
556015516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
556115516c77SSepherosa Ziehau 	    "Size of the packet for direct transmission");
556215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "sched_tx",
556315516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
556415516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_sched_tx),
556515516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
556615516c77SSepherosa Ziehau 	    "Always schedule transmission "
556715516c77SSepherosa Ziehau 	    "instead of doing direct transmission");
556815516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_cnt",
556915516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_cnt, 0, "# created TX rings");
557015516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_inuse",
557115516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_inuse, 0, "# used TX rings");
5572dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "agg_szmax",
5573dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_agg_szmax, 0,
5574dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation size");
5575dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pktmax",
5576dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5577dc13fee6SSepherosa Ziehau 	    hn_txagg_pktmax_sysctl, "I",
5578dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation packets");
5579dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_align",
5580dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
5581dc13fee6SSepherosa Ziehau 	    hn_txagg_align_sysctl, "I",
5582dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation alignment");
558315516c77SSepherosa Ziehau 
558415516c77SSepherosa Ziehau 	return 0;
558515516c77SSepherosa Ziehau }
558615516c77SSepherosa Ziehau 
558715516c77SSepherosa Ziehau static void
558815516c77SSepherosa Ziehau hn_set_chim_size(struct hn_softc *sc, int chim_size)
558915516c77SSepherosa Ziehau {
559015516c77SSepherosa Ziehau 	int i;
559115516c77SSepherosa Ziehau 
5592a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
559315516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_chim_size = chim_size;
559415516c77SSepherosa Ziehau }
559515516c77SSepherosa Ziehau 
559615516c77SSepherosa Ziehau static void
559715516c77SSepherosa Ziehau hn_set_tso_maxsize(struct hn_softc *sc, int tso_maxlen, int mtu)
559815516c77SSepherosa Ziehau {
559915516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
56009c6cae24SSepherosa Ziehau 	u_int hw_tsomax;
560115516c77SSepherosa Ziehau 	int tso_minlen;
560215516c77SSepherosa Ziehau 
56039c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
56049c6cae24SSepherosa Ziehau 
560515516c77SSepherosa Ziehau 	if ((ifp->if_capabilities & (IFCAP_TSO4 | IFCAP_TSO6)) == 0)
560615516c77SSepherosa Ziehau 		return;
560715516c77SSepherosa Ziehau 
560815516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_sgmin >= 2,
560915516c77SSepherosa Ziehau 	    ("invalid NDIS tso sgmin %d", sc->hn_ndis_tso_sgmin));
561015516c77SSepherosa Ziehau 	tso_minlen = sc->hn_ndis_tso_sgmin * mtu;
561115516c77SSepherosa Ziehau 
561215516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_szmax >= tso_minlen &&
561315516c77SSepherosa Ziehau 	    sc->hn_ndis_tso_szmax <= IP_MAXPACKET,
561415516c77SSepherosa Ziehau 	    ("invalid NDIS tso szmax %d", sc->hn_ndis_tso_szmax));
561515516c77SSepherosa Ziehau 
561615516c77SSepherosa Ziehau 	if (tso_maxlen < tso_minlen)
561715516c77SSepherosa Ziehau 		tso_maxlen = tso_minlen;
561815516c77SSepherosa Ziehau 	else if (tso_maxlen > IP_MAXPACKET)
561915516c77SSepherosa Ziehau 		tso_maxlen = IP_MAXPACKET;
562015516c77SSepherosa Ziehau 	if (tso_maxlen > sc->hn_ndis_tso_szmax)
562115516c77SSepherosa Ziehau 		tso_maxlen = sc->hn_ndis_tso_szmax;
56229c6cae24SSepherosa Ziehau 	hw_tsomax = tso_maxlen - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
56239c6cae24SSepherosa Ziehau 
56249c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
56259c6cae24SSepherosa Ziehau 		if (hw_tsomax > sc->hn_vf_ifp->if_hw_tsomax)
56269c6cae24SSepherosa Ziehau 			hw_tsomax = sc->hn_vf_ifp->if_hw_tsomax;
56279c6cae24SSepherosa Ziehau 	}
56289c6cae24SSepherosa Ziehau 	ifp->if_hw_tsomax = hw_tsomax;
562915516c77SSepherosa Ziehau 	if (bootverbose)
563015516c77SSepherosa Ziehau 		if_printf(ifp, "TSO size max %u\n", ifp->if_hw_tsomax);
563115516c77SSepherosa Ziehau }
563215516c77SSepherosa Ziehau 
563315516c77SSepherosa Ziehau static void
563415516c77SSepherosa Ziehau hn_fixup_tx_data(struct hn_softc *sc)
563515516c77SSepherosa Ziehau {
563615516c77SSepherosa Ziehau 	uint64_t csum_assist;
563715516c77SSepherosa Ziehau 	int i;
563815516c77SSepherosa Ziehau 
563915516c77SSepherosa Ziehau 	hn_set_chim_size(sc, sc->hn_chim_szmax);
564015516c77SSepherosa Ziehau 	if (hn_tx_chimney_size > 0 &&
564115516c77SSepherosa Ziehau 	    hn_tx_chimney_size < sc->hn_chim_szmax)
564215516c77SSepherosa Ziehau 		hn_set_chim_size(sc, hn_tx_chimney_size);
564315516c77SSepherosa Ziehau 
564415516c77SSepherosa Ziehau 	csum_assist = 0;
564515516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_IPCS)
564615516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP;
564715516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP4CS)
564815516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_TCP;
56492be266caSSepherosa Ziehau 	if ((sc->hn_caps & HN_CAP_UDP4CS) && hn_enable_udp4cs)
565015516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_UDP;
565115516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP6CS)
565215516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_TCP;
56532be266caSSepherosa Ziehau 	if ((sc->hn_caps & HN_CAP_UDP6CS) && hn_enable_udp6cs)
565415516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_UDP;
565515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
565615516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_csum_assist = csum_assist;
565715516c77SSepherosa Ziehau 
565815516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_HASHVAL) {
565915516c77SSepherosa Ziehau 		/*
566015516c77SSepherosa Ziehau 		 * Support HASHVAL pktinfo on TX path.
566115516c77SSepherosa Ziehau 		 */
566215516c77SSepherosa Ziehau 		if (bootverbose)
566315516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "support HASHVAL pktinfo\n");
566415516c77SSepherosa Ziehau 		for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
566515516c77SSepherosa Ziehau 			sc->hn_tx_ring[i].hn_tx_flags |= HN_TX_FLAG_HASHVAL;
566615516c77SSepherosa Ziehau 	}
566715516c77SSepherosa Ziehau }
566815516c77SSepherosa Ziehau 
566915516c77SSepherosa Ziehau static void
5670db76829bSSepherosa Ziehau hn_fixup_rx_data(struct hn_softc *sc)
5671db76829bSSepherosa Ziehau {
5672db76829bSSepherosa Ziehau 
5673db76829bSSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_UDPHASH) {
5674db76829bSSepherosa Ziehau 		int i;
5675db76829bSSepherosa Ziehau 
5676db76829bSSepherosa Ziehau 		for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
5677db76829bSSepherosa Ziehau 			sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_UDP_HASH;
5678db76829bSSepherosa Ziehau 	}
5679db76829bSSepherosa Ziehau }
5680db76829bSSepherosa Ziehau 
5681db76829bSSepherosa Ziehau static void
568215516c77SSepherosa Ziehau hn_destroy_tx_data(struct hn_softc *sc)
568315516c77SSepherosa Ziehau {
568415516c77SSepherosa Ziehau 	int i;
568515516c77SSepherosa Ziehau 
568615516c77SSepherosa Ziehau 	if (sc->hn_chim != NULL) {
56872494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_CHIM_REF) == 0) {
568815516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_chim_dma, sc->hn_chim);
56892494d735SSepherosa Ziehau 		} else {
56902494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
56912494d735SSepherosa Ziehau 			    "chimney sending buffer is referenced");
56922494d735SSepherosa Ziehau 		}
569315516c77SSepherosa Ziehau 		sc->hn_chim = NULL;
569415516c77SSepherosa Ziehau 	}
569515516c77SSepherosa Ziehau 
569615516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt == 0)
569715516c77SSepherosa Ziehau 		return;
569815516c77SSepherosa Ziehau 
569915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
570015516c77SSepherosa Ziehau 		hn_tx_ring_destroy(&sc->hn_tx_ring[i]);
570115516c77SSepherosa Ziehau 
570215516c77SSepherosa Ziehau 	free(sc->hn_tx_ring, M_DEVBUF);
570315516c77SSepherosa Ziehau 	sc->hn_tx_ring = NULL;
570415516c77SSepherosa Ziehau 
570515516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = 0;
570615516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = 0;
570715516c77SSepherosa Ziehau }
570815516c77SSepherosa Ziehau 
570923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
571023bf9e15SSepherosa Ziehau 
571115516c77SSepherosa Ziehau static void
571215516c77SSepherosa Ziehau hn_start_taskfunc(void *xtxr, int pending __unused)
571315516c77SSepherosa Ziehau {
571415516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
571515516c77SSepherosa Ziehau 
571615516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
571715516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
571815516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
571915516c77SSepherosa Ziehau }
572015516c77SSepherosa Ziehau 
572123bf9e15SSepherosa Ziehau static int
572223bf9e15SSepherosa Ziehau hn_start_locked(struct hn_tx_ring *txr, int len)
572323bf9e15SSepherosa Ziehau {
572423bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
572523bf9e15SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
5726dc13fee6SSepherosa Ziehau 	int sched = 0;
572723bf9e15SSepherosa Ziehau 
572823bf9e15SSepherosa Ziehau 	KASSERT(hn_use_if_start,
572923bf9e15SSepherosa Ziehau 	    ("hn_start_locked is called, when if_start is disabled"));
573023bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
573123bf9e15SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
5732dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
573323bf9e15SSepherosa Ziehau 
573423bf9e15SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5735dc13fee6SSepherosa Ziehau 		return (0);
573623bf9e15SSepherosa Ziehau 
573723bf9e15SSepherosa Ziehau 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
573823bf9e15SSepherosa Ziehau 	    IFF_DRV_RUNNING)
5739dc13fee6SSepherosa Ziehau 		return (0);
574023bf9e15SSepherosa Ziehau 
574123bf9e15SSepherosa Ziehau 	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
574223bf9e15SSepherosa Ziehau 		struct hn_txdesc *txd;
574323bf9e15SSepherosa Ziehau 		struct mbuf *m_head;
574423bf9e15SSepherosa Ziehau 		int error;
574523bf9e15SSepherosa Ziehau 
574623bf9e15SSepherosa Ziehau 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
574723bf9e15SSepherosa Ziehau 		if (m_head == NULL)
574823bf9e15SSepherosa Ziehau 			break;
574923bf9e15SSepherosa Ziehau 
575023bf9e15SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
575123bf9e15SSepherosa Ziehau 			/*
575223bf9e15SSepherosa Ziehau 			 * This sending could be time consuming; let callers
575323bf9e15SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
575423bf9e15SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
575523bf9e15SSepherosa Ziehau 			 */
575623bf9e15SSepherosa Ziehau 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
5757dc13fee6SSepherosa Ziehau 			sched = 1;
5758dc13fee6SSepherosa Ziehau 			break;
575923bf9e15SSepherosa Ziehau 		}
576023bf9e15SSepherosa Ziehau 
5761edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
5762edd3f315SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
5763edd3f315SSepherosa Ziehau 			m_head = hn_tso_fixup(m_head);
5764edd3f315SSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5765edd3f315SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5766edd3f315SSepherosa Ziehau 				continue;
5767edd3f315SSepherosa Ziehau 			}
5768c49d47daSSepherosa Ziehau 		} else if (m_head->m_pkthdr.csum_flags &
5769c49d47daSSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP)) {
5770c49d47daSSepherosa Ziehau 			m_head = hn_set_hlen(m_head);
5771c49d47daSSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5772c49d47daSSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5773c49d47daSSepherosa Ziehau 				continue;
5774c49d47daSSepherosa Ziehau 			}
5775edd3f315SSepherosa Ziehau 		}
5776edd3f315SSepherosa Ziehau #endif
5777edd3f315SSepherosa Ziehau 
577823bf9e15SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
577923bf9e15SSepherosa Ziehau 		if (txd == NULL) {
578023bf9e15SSepherosa Ziehau 			txr->hn_no_txdescs++;
578123bf9e15SSepherosa Ziehau 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
578223bf9e15SSepherosa Ziehau 			atomic_set_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
578323bf9e15SSepherosa Ziehau 			break;
578423bf9e15SSepherosa Ziehau 		}
578523bf9e15SSepherosa Ziehau 
5786dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
578723bf9e15SSepherosa Ziehau 		if (error) {
578823bf9e15SSepherosa Ziehau 			/* Both txd and m_head are freed */
5789dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5790dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
579123bf9e15SSepherosa Ziehau 			continue;
579223bf9e15SSepherosa Ziehau 		}
579323bf9e15SSepherosa Ziehau 
5794dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5795dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5796dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5797dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5798dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
5799dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5800dc13fee6SSepherosa Ziehau 					atomic_set_int(&ifp->if_drv_flags,
5801dc13fee6SSepherosa Ziehau 					    IFF_DRV_OACTIVE);
5802dc13fee6SSepherosa Ziehau 					break;
5803dc13fee6SSepherosa Ziehau 				}
5804dc13fee6SSepherosa Ziehau 			} else {
5805dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
580623bf9e15SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
580723bf9e15SSepherosa Ziehau 				if (__predict_false(error)) {
580823bf9e15SSepherosa Ziehau 					/* txd is freed, but m_head is not */
580923bf9e15SSepherosa Ziehau 					IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
5810dc13fee6SSepherosa Ziehau 					atomic_set_int(&ifp->if_drv_flags,
5811dc13fee6SSepherosa Ziehau 					    IFF_DRV_OACTIVE);
581223bf9e15SSepherosa Ziehau 					break;
581323bf9e15SSepherosa Ziehau 				}
581423bf9e15SSepherosa Ziehau 			}
5815dc13fee6SSepherosa Ziehau 		}
5816dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5817dc13fee6SSepherosa Ziehau 		else {
5818dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5819dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5820dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5821dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5822dc13fee6SSepherosa Ziehau 		}
5823dc13fee6SSepherosa Ziehau #endif
5824dc13fee6SSepherosa Ziehau 	}
5825dc13fee6SSepherosa Ziehau 
5826dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5827dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5828dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5829dc13fee6SSepherosa Ziehau 	return (sched);
583023bf9e15SSepherosa Ziehau }
583123bf9e15SSepherosa Ziehau 
583223bf9e15SSepherosa Ziehau static void
583323bf9e15SSepherosa Ziehau hn_start(struct ifnet *ifp)
583423bf9e15SSepherosa Ziehau {
583523bf9e15SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
583623bf9e15SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[0];
583723bf9e15SSepherosa Ziehau 
583823bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
583923bf9e15SSepherosa Ziehau 		goto do_sched;
584023bf9e15SSepherosa Ziehau 
584123bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
584223bf9e15SSepherosa Ziehau 		int sched;
584323bf9e15SSepherosa Ziehau 
584423bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
584523bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
584623bf9e15SSepherosa Ziehau 		if (!sched)
584723bf9e15SSepherosa Ziehau 			return;
584823bf9e15SSepherosa Ziehau 	}
584923bf9e15SSepherosa Ziehau do_sched:
585023bf9e15SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
585123bf9e15SSepherosa Ziehau }
585223bf9e15SSepherosa Ziehau 
585315516c77SSepherosa Ziehau static void
585415516c77SSepherosa Ziehau hn_start_txeof_taskfunc(void *xtxr, int pending __unused)
585515516c77SSepherosa Ziehau {
585615516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
585715516c77SSepherosa Ziehau 
585815516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
585915516c77SSepherosa Ziehau 	atomic_clear_int(&txr->hn_sc->hn_ifp->if_drv_flags, IFF_DRV_OACTIVE);
586015516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
586115516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
586215516c77SSepherosa Ziehau }
586315516c77SSepherosa Ziehau 
586423bf9e15SSepherosa Ziehau static void
586523bf9e15SSepherosa Ziehau hn_start_txeof(struct hn_tx_ring *txr)
586623bf9e15SSepherosa Ziehau {
586723bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
586823bf9e15SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
586923bf9e15SSepherosa Ziehau 
587023bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
587123bf9e15SSepherosa Ziehau 
587223bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
587323bf9e15SSepherosa Ziehau 		goto do_sched;
587423bf9e15SSepherosa Ziehau 
587523bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
587623bf9e15SSepherosa Ziehau 		int sched;
587723bf9e15SSepherosa Ziehau 
587823bf9e15SSepherosa Ziehau 		atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
587923bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
588023bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
588123bf9e15SSepherosa Ziehau 		if (sched) {
588223bf9e15SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
588323bf9e15SSepherosa Ziehau 			    &txr->hn_tx_task);
588423bf9e15SSepherosa Ziehau 		}
588523bf9e15SSepherosa Ziehau 	} else {
588623bf9e15SSepherosa Ziehau do_sched:
588723bf9e15SSepherosa Ziehau 		/*
588823bf9e15SSepherosa Ziehau 		 * Release the OACTIVE earlier, with the hope, that
588923bf9e15SSepherosa Ziehau 		 * others could catch up.  The task will clear the
589023bf9e15SSepherosa Ziehau 		 * flag again with the hn_tx_lock to avoid possible
589123bf9e15SSepherosa Ziehau 		 * races.
589223bf9e15SSepherosa Ziehau 		 */
589323bf9e15SSepherosa Ziehau 		atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
589423bf9e15SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
589523bf9e15SSepherosa Ziehau 	}
589623bf9e15SSepherosa Ziehau }
589723bf9e15SSepherosa Ziehau 
589823bf9e15SSepherosa Ziehau #endif	/* HN_IFSTART_SUPPORT */
589923bf9e15SSepherosa Ziehau 
590015516c77SSepherosa Ziehau static int
590115516c77SSepherosa Ziehau hn_xmit(struct hn_tx_ring *txr, int len)
590215516c77SSepherosa Ziehau {
590315516c77SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
590415516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
590515516c77SSepherosa Ziehau 	struct mbuf *m_head;
5906dc13fee6SSepherosa Ziehau 	int sched = 0;
590715516c77SSepherosa Ziehau 
590815516c77SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
590923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
591015516c77SSepherosa Ziehau 	KASSERT(hn_use_if_start == 0,
591115516c77SSepherosa Ziehau 	    ("hn_xmit is called, when if_start is enabled"));
591223bf9e15SSepherosa Ziehau #endif
5913dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
591415516c77SSepherosa Ziehau 
591515516c77SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5916dc13fee6SSepherosa Ziehau 		return (0);
591715516c77SSepherosa Ziehau 
591815516c77SSepherosa Ziehau 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || txr->hn_oactive)
5919dc13fee6SSepherosa Ziehau 		return (0);
592015516c77SSepherosa Ziehau 
592115516c77SSepherosa Ziehau 	while ((m_head = drbr_peek(ifp, txr->hn_mbuf_br)) != NULL) {
592215516c77SSepherosa Ziehau 		struct hn_txdesc *txd;
592315516c77SSepherosa Ziehau 		int error;
592415516c77SSepherosa Ziehau 
592515516c77SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
592615516c77SSepherosa Ziehau 			/*
592715516c77SSepherosa Ziehau 			 * This sending could be time consuming; let callers
592815516c77SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
592915516c77SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
593015516c77SSepherosa Ziehau 			 */
593115516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
5932dc13fee6SSepherosa Ziehau 			sched = 1;
5933dc13fee6SSepherosa Ziehau 			break;
593415516c77SSepherosa Ziehau 		}
593515516c77SSepherosa Ziehau 
593615516c77SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
593715516c77SSepherosa Ziehau 		if (txd == NULL) {
593815516c77SSepherosa Ziehau 			txr->hn_no_txdescs++;
593915516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
594015516c77SSepherosa Ziehau 			txr->hn_oactive = 1;
594115516c77SSepherosa Ziehau 			break;
594215516c77SSepherosa Ziehau 		}
594315516c77SSepherosa Ziehau 
5944dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
594515516c77SSepherosa Ziehau 		if (error) {
594615516c77SSepherosa Ziehau 			/* Both txd and m_head are freed; discard */
5947dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5948dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
594915516c77SSepherosa Ziehau 			drbr_advance(ifp, txr->hn_mbuf_br);
595015516c77SSepherosa Ziehau 			continue;
595115516c77SSepherosa Ziehau 		}
595215516c77SSepherosa Ziehau 
5953dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5954dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5955dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5956dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5957dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
595815516c77SSepherosa Ziehau 				if (__predict_false(error)) {
595915516c77SSepherosa Ziehau 					txr->hn_oactive = 1;
596015516c77SSepherosa Ziehau 					break;
596115516c77SSepherosa Ziehau 				}
5962dc13fee6SSepherosa Ziehau 			} else {
5963dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
5964dc13fee6SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
5965dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5966dc13fee6SSepherosa Ziehau 					/* txd is freed, but m_head is not */
5967dc13fee6SSepherosa Ziehau 					drbr_putback(ifp, txr->hn_mbuf_br,
5968dc13fee6SSepherosa Ziehau 					    m_head);
5969dc13fee6SSepherosa Ziehau 					txr->hn_oactive = 1;
5970dc13fee6SSepherosa Ziehau 					break;
5971dc13fee6SSepherosa Ziehau 				}
5972dc13fee6SSepherosa Ziehau 			}
5973dc13fee6SSepherosa Ziehau 		}
5974dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5975dc13fee6SSepherosa Ziehau 		else {
5976dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5977dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5978dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5979dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5980dc13fee6SSepherosa Ziehau 		}
5981dc13fee6SSepherosa Ziehau #endif
598215516c77SSepherosa Ziehau 
598315516c77SSepherosa Ziehau 		/* Sent */
598415516c77SSepherosa Ziehau 		drbr_advance(ifp, txr->hn_mbuf_br);
598515516c77SSepherosa Ziehau 	}
5986dc13fee6SSepherosa Ziehau 
5987dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5988dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5989dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5990dc13fee6SSepherosa Ziehau 	return (sched);
599115516c77SSepherosa Ziehau }
599215516c77SSepherosa Ziehau 
599315516c77SSepherosa Ziehau static int
599415516c77SSepherosa Ziehau hn_transmit(struct ifnet *ifp, struct mbuf *m)
599515516c77SSepherosa Ziehau {
599615516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
599715516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
599815516c77SSepherosa Ziehau 	int error, idx = 0;
599915516c77SSepherosa Ziehau 
60009c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
60019c6cae24SSepherosa Ziehau 		struct rm_priotracker pt;
60029c6cae24SSepherosa Ziehau 
60039c6cae24SSepherosa Ziehau 		rm_rlock(&sc->hn_vf_lock, &pt);
60049c6cae24SSepherosa Ziehau 		if (__predict_true(sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
60059c6cae24SSepherosa Ziehau 			struct mbuf *m_bpf = NULL;
60069c6cae24SSepherosa Ziehau 			int obytes, omcast;
60079c6cae24SSepherosa Ziehau 
60089c6cae24SSepherosa Ziehau 			obytes = m->m_pkthdr.len;
60097898a1f4SEric van Gyzen 			omcast = (m->m_flags & M_MCAST) != 0;
60109c6cae24SSepherosa Ziehau 
60119c6cae24SSepherosa Ziehau 			if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF) {
60129c6cae24SSepherosa Ziehau 				if (bpf_peers_present(ifp->if_bpf)) {
60139c6cae24SSepherosa Ziehau 					m_bpf = m_copypacket(m, M_NOWAIT);
60149c6cae24SSepherosa Ziehau 					if (m_bpf == NULL) {
60159c6cae24SSepherosa Ziehau 						/*
60169c6cae24SSepherosa Ziehau 						 * Failed to grab a shallow
60179c6cae24SSepherosa Ziehau 						 * copy; tap now.
60189c6cae24SSepherosa Ziehau 						 */
60199c6cae24SSepherosa Ziehau 						ETHER_BPF_MTAP(ifp, m);
60209c6cae24SSepherosa Ziehau 					}
60219c6cae24SSepherosa Ziehau 				}
60229c6cae24SSepherosa Ziehau 			} else {
60239c6cae24SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, m);
60249c6cae24SSepherosa Ziehau 			}
60259c6cae24SSepherosa Ziehau 
60269c6cae24SSepherosa Ziehau 			error = sc->hn_vf_ifp->if_transmit(sc->hn_vf_ifp, m);
60279c6cae24SSepherosa Ziehau 			rm_runlock(&sc->hn_vf_lock, &pt);
60289c6cae24SSepherosa Ziehau 
60299c6cae24SSepherosa Ziehau 			if (m_bpf != NULL) {
60309c6cae24SSepherosa Ziehau 				if (!error)
60319c6cae24SSepherosa Ziehau 					ETHER_BPF_MTAP(ifp, m_bpf);
60329c6cae24SSepherosa Ziehau 				m_freem(m_bpf);
60339c6cae24SSepherosa Ziehau 			}
60349c6cae24SSepherosa Ziehau 
60359c6cae24SSepherosa Ziehau 			if (error == ENOBUFS) {
60369c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
60379c6cae24SSepherosa Ziehau 			} else if (error) {
60389c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
60399c6cae24SSepherosa Ziehau 			} else {
60409c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
60419c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OBYTES, obytes);
60429c6cae24SSepherosa Ziehau 				if (omcast) {
60439c6cae24SSepherosa Ziehau 					if_inc_counter(ifp, IFCOUNTER_OMCASTS,
60449c6cae24SSepherosa Ziehau 					    omcast);
60459c6cae24SSepherosa Ziehau 				}
60469c6cae24SSepherosa Ziehau 			}
60479c6cae24SSepherosa Ziehau 			return (error);
60489c6cae24SSepherosa Ziehau 		}
60499c6cae24SSepherosa Ziehau 		rm_runlock(&sc->hn_vf_lock, &pt);
60509c6cae24SSepherosa Ziehau 	}
60519c6cae24SSepherosa Ziehau 
6052edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
6053edd3f315SSepherosa Ziehau 	/*
6054c49d47daSSepherosa Ziehau 	 * Perform TSO packet header fixup or get l2/l3 header length now,
6055c49d47daSSepherosa Ziehau 	 * since packet headers should be cache-hot.
6056edd3f315SSepherosa Ziehau 	 */
6057edd3f315SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
6058edd3f315SSepherosa Ziehau 		m = hn_tso_fixup(m);
6059edd3f315SSepherosa Ziehau 		if (__predict_false(m == NULL)) {
6060edd3f315SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
6061edd3f315SSepherosa Ziehau 			return EIO;
6062edd3f315SSepherosa Ziehau 		}
6063c49d47daSSepherosa Ziehau 	} else if (m->m_pkthdr.csum_flags &
6064c49d47daSSepherosa Ziehau 	    (CSUM_IP_UDP | CSUM_IP_TCP | CSUM_IP6_UDP | CSUM_IP6_TCP)) {
6065c49d47daSSepherosa Ziehau 		m = hn_set_hlen(m);
6066c49d47daSSepherosa Ziehau 		if (__predict_false(m == NULL)) {
6067c49d47daSSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
6068c49d47daSSepherosa Ziehau 			return EIO;
6069c49d47daSSepherosa Ziehau 		}
6070edd3f315SSepherosa Ziehau 	}
6071edd3f315SSepherosa Ziehau #endif
6072edd3f315SSepherosa Ziehau 
607315516c77SSepherosa Ziehau 	/*
607415516c77SSepherosa Ziehau 	 * Select the TX ring based on flowid
607515516c77SSepherosa Ziehau 	 */
607634d68912SSepherosa Ziehau 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
607734d68912SSepherosa Ziehau #ifdef RSS
607834d68912SSepherosa Ziehau 		uint32_t bid;
607934d68912SSepherosa Ziehau 
608034d68912SSepherosa Ziehau 		if (rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m),
608134d68912SSepherosa Ziehau 		    &bid) == 0)
608234d68912SSepherosa Ziehau 			idx = bid % sc->hn_tx_ring_inuse;
608334d68912SSepherosa Ziehau 		else
608434d68912SSepherosa Ziehau #endif
6085cc0c6ebcSSepherosa Ziehau 		{
6086cc0c6ebcSSepherosa Ziehau #if defined(INET6) || defined(INET)
6087cc0c6ebcSSepherosa Ziehau 			int tcpsyn = 0;
6088cc0c6ebcSSepherosa Ziehau 
6089cc0c6ebcSSepherosa Ziehau 			if (m->m_pkthdr.len < 128 &&
6090cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags &
6091cc0c6ebcSSepherosa Ziehau 			     (CSUM_IP_TCP | CSUM_IP6_TCP)) &&
6092cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
6093cc0c6ebcSSepherosa Ziehau 				m = hn_check_tcpsyn(m, &tcpsyn);
6094cc0c6ebcSSepherosa Ziehau 				if (__predict_false(m == NULL)) {
6095cc0c6ebcSSepherosa Ziehau 					if_inc_counter(ifp,
6096cc0c6ebcSSepherosa Ziehau 					    IFCOUNTER_OERRORS, 1);
6097cc0c6ebcSSepherosa Ziehau 					return (EIO);
6098cc0c6ebcSSepherosa Ziehau 				}
6099cc0c6ebcSSepherosa Ziehau 			}
6100cc0c6ebcSSepherosa Ziehau #else
6101cc0c6ebcSSepherosa Ziehau 			const int tcpsyn = 0;
6102cc0c6ebcSSepherosa Ziehau #endif
6103cc0c6ebcSSepherosa Ziehau 			if (tcpsyn)
6104cc0c6ebcSSepherosa Ziehau 				idx = 0;
6105cc0c6ebcSSepherosa Ziehau 			else
610615516c77SSepherosa Ziehau 				idx = m->m_pkthdr.flowid % sc->hn_tx_ring_inuse;
610734d68912SSepherosa Ziehau 		}
6108cc0c6ebcSSepherosa Ziehau 	}
610915516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[idx];
611015516c77SSepherosa Ziehau 
611115516c77SSepherosa Ziehau 	error = drbr_enqueue(ifp, txr->hn_mbuf_br, m);
611215516c77SSepherosa Ziehau 	if (error) {
611315516c77SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
611415516c77SSepherosa Ziehau 		return error;
611515516c77SSepherosa Ziehau 	}
611615516c77SSepherosa Ziehau 
611715516c77SSepherosa Ziehau 	if (txr->hn_oactive)
611815516c77SSepherosa Ziehau 		return 0;
611915516c77SSepherosa Ziehau 
612015516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
612115516c77SSepherosa Ziehau 		goto do_sched;
612215516c77SSepherosa Ziehau 
612315516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
612415516c77SSepherosa Ziehau 		int sched;
612515516c77SSepherosa Ziehau 
612615516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
612715516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
612815516c77SSepherosa Ziehau 		if (!sched)
612915516c77SSepherosa Ziehau 			return 0;
613015516c77SSepherosa Ziehau 	}
613115516c77SSepherosa Ziehau do_sched:
613215516c77SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
613315516c77SSepherosa Ziehau 	return 0;
613415516c77SSepherosa Ziehau }
613515516c77SSepherosa Ziehau 
613615516c77SSepherosa Ziehau static void
613715516c77SSepherosa Ziehau hn_tx_ring_qflush(struct hn_tx_ring *txr)
613815516c77SSepherosa Ziehau {
613915516c77SSepherosa Ziehau 	struct mbuf *m;
614015516c77SSepherosa Ziehau 
614115516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
614215516c77SSepherosa Ziehau 	while ((m = buf_ring_dequeue_sc(txr->hn_mbuf_br)) != NULL)
614315516c77SSepherosa Ziehau 		m_freem(m);
614415516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
614515516c77SSepherosa Ziehau }
614615516c77SSepherosa Ziehau 
614715516c77SSepherosa Ziehau static void
614815516c77SSepherosa Ziehau hn_xmit_qflush(struct ifnet *ifp)
614915516c77SSepherosa Ziehau {
615015516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
61519c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
615215516c77SSepherosa Ziehau 	int i;
615315516c77SSepherosa Ziehau 
615415516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
615515516c77SSepherosa Ziehau 		hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
615615516c77SSepherosa Ziehau 	if_qflush(ifp);
61579c6cae24SSepherosa Ziehau 
61589c6cae24SSepherosa Ziehau 	rm_rlock(&sc->hn_vf_lock, &pt);
61599c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
61609c6cae24SSepherosa Ziehau 		sc->hn_vf_ifp->if_qflush(sc->hn_vf_ifp);
61619c6cae24SSepherosa Ziehau 	rm_runlock(&sc->hn_vf_lock, &pt);
616215516c77SSepherosa Ziehau }
616315516c77SSepherosa Ziehau 
616415516c77SSepherosa Ziehau static void
616515516c77SSepherosa Ziehau hn_xmit_txeof(struct hn_tx_ring *txr)
616615516c77SSepherosa Ziehau {
616715516c77SSepherosa Ziehau 
616815516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
616915516c77SSepherosa Ziehau 		goto do_sched;
617015516c77SSepherosa Ziehau 
617115516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
617215516c77SSepherosa Ziehau 		int sched;
617315516c77SSepherosa Ziehau 
617415516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
617515516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
617615516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
617715516c77SSepherosa Ziehau 		if (sched) {
617815516c77SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
617915516c77SSepherosa Ziehau 			    &txr->hn_tx_task);
618015516c77SSepherosa Ziehau 		}
618115516c77SSepherosa Ziehau 	} else {
618215516c77SSepherosa Ziehau do_sched:
618315516c77SSepherosa Ziehau 		/*
618415516c77SSepherosa Ziehau 		 * Release the oactive earlier, with the hope, that
618515516c77SSepherosa Ziehau 		 * others could catch up.  The task will clear the
618615516c77SSepherosa Ziehau 		 * oactive again with the hn_tx_lock to avoid possible
618715516c77SSepherosa Ziehau 		 * races.
618815516c77SSepherosa Ziehau 		 */
618915516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
619015516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
619115516c77SSepherosa Ziehau 	}
619215516c77SSepherosa Ziehau }
619315516c77SSepherosa Ziehau 
619415516c77SSepherosa Ziehau static void
619515516c77SSepherosa Ziehau hn_xmit_taskfunc(void *xtxr, int pending __unused)
619615516c77SSepherosa Ziehau {
619715516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
619815516c77SSepherosa Ziehau 
619915516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
620015516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
620115516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
620215516c77SSepherosa Ziehau }
620315516c77SSepherosa Ziehau 
620415516c77SSepherosa Ziehau static void
620515516c77SSepherosa Ziehau hn_xmit_txeof_taskfunc(void *xtxr, int pending __unused)
620615516c77SSepherosa Ziehau {
620715516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
620815516c77SSepherosa Ziehau 
620915516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
621015516c77SSepherosa Ziehau 	txr->hn_oactive = 0;
621115516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
621215516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
621315516c77SSepherosa Ziehau }
621415516c77SSepherosa Ziehau 
621515516c77SSepherosa Ziehau static int
621615516c77SSepherosa Ziehau hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
621715516c77SSepherosa Ziehau {
621815516c77SSepherosa Ziehau 	struct vmbus_chan_br cbr;
621915516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
622015516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = NULL;
622115516c77SSepherosa Ziehau 	int idx, error;
622215516c77SSepherosa Ziehau 
622315516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
622415516c77SSepherosa Ziehau 
622515516c77SSepherosa Ziehau 	/*
622615516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
622715516c77SSepherosa Ziehau 	 */
622815516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
622915516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
623015516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
623115516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
623215516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED) == 0,
623315516c77SSepherosa Ziehau 	    ("RX ring %d already attached", idx));
623415516c77SSepherosa Ziehau 	rxr->hn_rx_flags |= HN_RX_FLAG_ATTACHED;
62353ab0fea1SDexuan Cui 	rxr->hn_chan = chan;
623615516c77SSepherosa Ziehau 
623715516c77SSepherosa Ziehau 	if (bootverbose) {
623815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "link RX ring %d to chan%u\n",
623915516c77SSepherosa Ziehau 		    idx, vmbus_chan_id(chan));
624015516c77SSepherosa Ziehau 	}
624115516c77SSepherosa Ziehau 
624215516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
624315516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[idx];
624415516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED) == 0,
624515516c77SSepherosa Ziehau 		    ("TX ring %d already attached", idx));
624615516c77SSepherosa Ziehau 		txr->hn_tx_flags |= HN_TX_FLAG_ATTACHED;
624715516c77SSepherosa Ziehau 
624815516c77SSepherosa Ziehau 		txr->hn_chan = chan;
624915516c77SSepherosa Ziehau 		if (bootverbose) {
625015516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "link TX ring %d to chan%u\n",
625115516c77SSepherosa Ziehau 			    idx, vmbus_chan_id(chan));
625215516c77SSepherosa Ziehau 		}
625315516c77SSepherosa Ziehau 	}
625415516c77SSepherosa Ziehau 
625515516c77SSepherosa Ziehau 	/* Bind this channel to a proper CPU. */
62560e11868dSSepherosa Ziehau 	vmbus_chan_cpu_set(chan, HN_RING_IDX2CPU(sc, idx));
625715516c77SSepherosa Ziehau 
625815516c77SSepherosa Ziehau 	/*
625915516c77SSepherosa Ziehau 	 * Open this channel
626015516c77SSepherosa Ziehau 	 */
626115516c77SSepherosa Ziehau 	cbr.cbr = rxr->hn_br;
626215516c77SSepherosa Ziehau 	cbr.cbr_paddr = rxr->hn_br_dma.hv_paddr;
626315516c77SSepherosa Ziehau 	cbr.cbr_txsz = HN_TXBR_SIZE;
626415516c77SSepherosa Ziehau 	cbr.cbr_rxsz = HN_RXBR_SIZE;
626515516c77SSepherosa Ziehau 	error = vmbus_chan_open_br(chan, &cbr, NULL, 0, hn_chan_callback, rxr);
626615516c77SSepherosa Ziehau 	if (error) {
626771e8ac56SSepherosa Ziehau 		if (error == EISCONN) {
626871e8ac56SSepherosa Ziehau 			if_printf(sc->hn_ifp, "bufring is connected after "
626971e8ac56SSepherosa Ziehau 			    "chan%u open failure\n", vmbus_chan_id(chan));
627071e8ac56SSepherosa Ziehau 			rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
627171e8ac56SSepherosa Ziehau 		} else {
627215516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "open chan%u failed: %d\n",
627315516c77SSepherosa Ziehau 			    vmbus_chan_id(chan), error);
627471e8ac56SSepherosa Ziehau 		}
627515516c77SSepherosa Ziehau 	}
627615516c77SSepherosa Ziehau 	return (error);
627715516c77SSepherosa Ziehau }
627815516c77SSepherosa Ziehau 
627915516c77SSepherosa Ziehau static void
628015516c77SSepherosa Ziehau hn_chan_detach(struct hn_softc *sc, struct vmbus_channel *chan)
628115516c77SSepherosa Ziehau {
628215516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
62832494d735SSepherosa Ziehau 	int idx, error;
628415516c77SSepherosa Ziehau 
628515516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
628615516c77SSepherosa Ziehau 
628715516c77SSepherosa Ziehau 	/*
628815516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
628915516c77SSepherosa Ziehau 	 */
629015516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
629115516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
629215516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
629315516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
629415516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED),
629515516c77SSepherosa Ziehau 	    ("RX ring %d is not attached", idx));
629615516c77SSepherosa Ziehau 	rxr->hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
629715516c77SSepherosa Ziehau 
629815516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
629915516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[idx];
630015516c77SSepherosa Ziehau 
630115516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED),
630215516c77SSepherosa Ziehau 		    ("TX ring %d is not attached attached", idx));
630315516c77SSepherosa Ziehau 		txr->hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
630415516c77SSepherosa Ziehau 	}
630515516c77SSepherosa Ziehau 
630615516c77SSepherosa Ziehau 	/*
630715516c77SSepherosa Ziehau 	 * Close this channel.
630815516c77SSepherosa Ziehau 	 *
630915516c77SSepherosa Ziehau 	 * NOTE:
631015516c77SSepherosa Ziehau 	 * Channel closing does _not_ destroy the target channel.
631115516c77SSepherosa Ziehau 	 */
63122494d735SSepherosa Ziehau 	error = vmbus_chan_close_direct(chan);
63132494d735SSepherosa Ziehau 	if (error == EISCONN) {
6314aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u bufring is connected "
6315aa1a2adcSSepherosa Ziehau 		    "after being closed\n", vmbus_chan_id(chan));
63162494d735SSepherosa Ziehau 		rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
63172494d735SSepherosa Ziehau 	} else if (error) {
6318aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u close failed: %d\n",
6319aa1a2adcSSepherosa Ziehau 		    vmbus_chan_id(chan), error);
63202494d735SSepherosa Ziehau 	}
632115516c77SSepherosa Ziehau }
632215516c77SSepherosa Ziehau 
632315516c77SSepherosa Ziehau static int
632415516c77SSepherosa Ziehau hn_attach_subchans(struct hn_softc *sc)
632515516c77SSepherosa Ziehau {
632615516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
632715516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
632815516c77SSepherosa Ziehau 	int i, error = 0;
632915516c77SSepherosa Ziehau 
633071e8ac56SSepherosa Ziehau 	KASSERT(subchan_cnt > 0, ("no sub-channels"));
633115516c77SSepherosa Ziehau 
633215516c77SSepherosa Ziehau 	/* Attach the sub-channels. */
633315516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
633415516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i) {
633571e8ac56SSepherosa Ziehau 		int error1;
633671e8ac56SSepherosa Ziehau 
633771e8ac56SSepherosa Ziehau 		error1 = hn_chan_attach(sc, subchans[i]);
633871e8ac56SSepherosa Ziehau 		if (error1) {
633971e8ac56SSepherosa Ziehau 			error = error1;
634071e8ac56SSepherosa Ziehau 			/* Move on; all channels will be detached later. */
634171e8ac56SSepherosa Ziehau 		}
634215516c77SSepherosa Ziehau 	}
634315516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
634415516c77SSepherosa Ziehau 
634515516c77SSepherosa Ziehau 	if (error) {
634615516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "sub-channels attach failed: %d\n", error);
634715516c77SSepherosa Ziehau 	} else {
634815516c77SSepherosa Ziehau 		if (bootverbose) {
634915516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "%d sub-channels attached\n",
635015516c77SSepherosa Ziehau 			    subchan_cnt);
635115516c77SSepherosa Ziehau 		}
635215516c77SSepherosa Ziehau 	}
635315516c77SSepherosa Ziehau 	return (error);
635415516c77SSepherosa Ziehau }
635515516c77SSepherosa Ziehau 
635615516c77SSepherosa Ziehau static void
635715516c77SSepherosa Ziehau hn_detach_allchans(struct hn_softc *sc)
635815516c77SSepherosa Ziehau {
635915516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
636015516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
636115516c77SSepherosa Ziehau 	int i;
636215516c77SSepherosa Ziehau 
636315516c77SSepherosa Ziehau 	if (subchan_cnt == 0)
636415516c77SSepherosa Ziehau 		goto back;
636515516c77SSepherosa Ziehau 
636615516c77SSepherosa Ziehau 	/* Detach the sub-channels. */
636715516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
636815516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i)
636915516c77SSepherosa Ziehau 		hn_chan_detach(sc, subchans[i]);
637015516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
637115516c77SSepherosa Ziehau 
637215516c77SSepherosa Ziehau back:
637315516c77SSepherosa Ziehau 	/*
637415516c77SSepherosa Ziehau 	 * Detach the primary channel, _after_ all sub-channels
637515516c77SSepherosa Ziehau 	 * are detached.
637615516c77SSepherosa Ziehau 	 */
637715516c77SSepherosa Ziehau 	hn_chan_detach(sc, sc->hn_prichan);
637815516c77SSepherosa Ziehau 
637915516c77SSepherosa Ziehau 	/* Wait for sub-channels to be destroyed, if any. */
638015516c77SSepherosa Ziehau 	vmbus_subchan_drain(sc->hn_prichan);
638115516c77SSepherosa Ziehau 
638215516c77SSepherosa Ziehau #ifdef INVARIANTS
638315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
638415516c77SSepherosa Ziehau 		KASSERT((sc->hn_rx_ring[i].hn_rx_flags &
638515516c77SSepherosa Ziehau 		    HN_RX_FLAG_ATTACHED) == 0,
638615516c77SSepherosa Ziehau 		    ("%dth RX ring is still attached", i));
638715516c77SSepherosa Ziehau 	}
638815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
638915516c77SSepherosa Ziehau 		KASSERT((sc->hn_tx_ring[i].hn_tx_flags &
639015516c77SSepherosa Ziehau 		    HN_TX_FLAG_ATTACHED) == 0,
639115516c77SSepherosa Ziehau 		    ("%dth TX ring is still attached", i));
639215516c77SSepherosa Ziehau 	}
639315516c77SSepherosa Ziehau #endif
639415516c77SSepherosa Ziehau }
639515516c77SSepherosa Ziehau 
639615516c77SSepherosa Ziehau static int
639715516c77SSepherosa Ziehau hn_synth_alloc_subchans(struct hn_softc *sc, int *nsubch)
639815516c77SSepherosa Ziehau {
639915516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
640015516c77SSepherosa Ziehau 	int nchan, rxr_cnt, error;
640115516c77SSepherosa Ziehau 
640215516c77SSepherosa Ziehau 	nchan = *nsubch + 1;
640315516c77SSepherosa Ziehau 	if (nchan == 1) {
640415516c77SSepherosa Ziehau 		/*
640515516c77SSepherosa Ziehau 		 * Multiple RX/TX rings are not requested.
640615516c77SSepherosa Ziehau 		 */
640715516c77SSepherosa Ziehau 		*nsubch = 0;
640815516c77SSepherosa Ziehau 		return (0);
640915516c77SSepherosa Ziehau 	}
641015516c77SSepherosa Ziehau 
641115516c77SSepherosa Ziehau 	/*
641215516c77SSepherosa Ziehau 	 * Query RSS capabilities, e.g. # of RX rings, and # of indirect
641315516c77SSepherosa Ziehau 	 * table entries.
641415516c77SSepherosa Ziehau 	 */
641515516c77SSepherosa Ziehau 	error = hn_rndis_query_rsscaps(sc, &rxr_cnt);
641615516c77SSepherosa Ziehau 	if (error) {
641715516c77SSepherosa Ziehau 		/* No RSS; this is benign. */
641815516c77SSepherosa Ziehau 		*nsubch = 0;
641915516c77SSepherosa Ziehau 		return (0);
642015516c77SSepherosa Ziehau 	}
642115516c77SSepherosa Ziehau 	if (bootverbose) {
642215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
642315516c77SSepherosa Ziehau 		    rxr_cnt, nchan);
642415516c77SSepherosa Ziehau 	}
642515516c77SSepherosa Ziehau 
642615516c77SSepherosa Ziehau 	if (nchan > rxr_cnt)
642715516c77SSepherosa Ziehau 		nchan = rxr_cnt;
642815516c77SSepherosa Ziehau 	if (nchan == 1) {
642915516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "only 1 channel is supported, no vRSS\n");
643015516c77SSepherosa Ziehau 		*nsubch = 0;
643115516c77SSepherosa Ziehau 		return (0);
643215516c77SSepherosa Ziehau 	}
643315516c77SSepherosa Ziehau 
643415516c77SSepherosa Ziehau 	/*
643515516c77SSepherosa Ziehau 	 * Allocate sub-channels from NVS.
643615516c77SSepherosa Ziehau 	 */
643715516c77SSepherosa Ziehau 	*nsubch = nchan - 1;
643815516c77SSepherosa Ziehau 	error = hn_nvs_alloc_subchans(sc, nsubch);
643915516c77SSepherosa Ziehau 	if (error || *nsubch == 0) {
644015516c77SSepherosa Ziehau 		/* Failed to allocate sub-channels. */
644115516c77SSepherosa Ziehau 		*nsubch = 0;
644215516c77SSepherosa Ziehau 		return (0);
644315516c77SSepherosa Ziehau 	}
644415516c77SSepherosa Ziehau 
644515516c77SSepherosa Ziehau 	/*
644615516c77SSepherosa Ziehau 	 * Wait for all sub-channels to become ready before moving on.
644715516c77SSepherosa Ziehau 	 */
644815516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, *nsubch);
644915516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, *nsubch);
645015516c77SSepherosa Ziehau 	return (0);
645115516c77SSepherosa Ziehau }
645215516c77SSepherosa Ziehau 
64532494d735SSepherosa Ziehau static bool
64542494d735SSepherosa Ziehau hn_synth_attachable(const struct hn_softc *sc)
64552494d735SSepherosa Ziehau {
64562494d735SSepherosa Ziehau 	int i;
64572494d735SSepherosa Ziehau 
64582494d735SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_ERRORS)
64592494d735SSepherosa Ziehau 		return (false);
64602494d735SSepherosa Ziehau 
64612494d735SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
64622494d735SSepherosa Ziehau 		const struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
64632494d735SSepherosa Ziehau 
64642494d735SSepherosa Ziehau 		if (rxr->hn_rx_flags & HN_RX_FLAG_BR_REF)
64652494d735SSepherosa Ziehau 			return (false);
64662494d735SSepherosa Ziehau 	}
64672494d735SSepherosa Ziehau 	return (true);
64682494d735SSepherosa Ziehau }
64692494d735SSepherosa Ziehau 
6470b3b75d9cSSepherosa Ziehau /*
6471b3b75d9cSSepherosa Ziehau  * Make sure that the RX filter is zero after the successful
6472b3b75d9cSSepherosa Ziehau  * RNDIS initialization.
6473b3b75d9cSSepherosa Ziehau  *
6474b3b75d9cSSepherosa Ziehau  * NOTE:
6475b3b75d9cSSepherosa Ziehau  * Under certain conditions on certain versions of Hyper-V,
6476b3b75d9cSSepherosa Ziehau  * the RNDIS rxfilter is _not_ zero on the hypervisor side
6477b3b75d9cSSepherosa Ziehau  * after the successful RNDIS initialization, which breaks
6478b3b75d9cSSepherosa Ziehau  * the assumption of any following code (well, it breaks the
6479b3b75d9cSSepherosa Ziehau  * RNDIS API contract actually).  Clear the RNDIS rxfilter
6480b3b75d9cSSepherosa Ziehau  * explicitly, drain packets sneaking through, and drain the
6481b3b75d9cSSepherosa Ziehau  * interrupt taskqueues scheduled due to the stealth packets.
6482b3b75d9cSSepherosa Ziehau  */
6483b3b75d9cSSepherosa Ziehau static void
6484b3b75d9cSSepherosa Ziehau hn_rndis_init_fixat(struct hn_softc *sc, int nchan)
6485b3b75d9cSSepherosa Ziehau {
6486b3b75d9cSSepherosa Ziehau 
6487b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
6488b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, nchan);
6489b3b75d9cSSepherosa Ziehau }
6490b3b75d9cSSepherosa Ziehau 
649115516c77SSepherosa Ziehau static int
649215516c77SSepherosa Ziehau hn_synth_attach(struct hn_softc *sc, int mtu)
649315516c77SSepherosa Ziehau {
649471e8ac56SSepherosa Ziehau #define ATTACHED_NVS		0x0002
649571e8ac56SSepherosa Ziehau #define ATTACHED_RNDIS		0x0004
649671e8ac56SSepherosa Ziehau 
649715516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
6498b3b75d9cSSepherosa Ziehau 	int error, nsubch, nchan = 1, i, rndis_inited;
649971e8ac56SSepherosa Ziehau 	uint32_t old_caps, attached = 0;
650015516c77SSepherosa Ziehau 
650115516c77SSepherosa Ziehau 	KASSERT((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0,
650215516c77SSepherosa Ziehau 	    ("synthetic parts were attached"));
650315516c77SSepherosa Ziehau 
65042494d735SSepherosa Ziehau 	if (!hn_synth_attachable(sc))
65052494d735SSepherosa Ziehau 		return (ENXIO);
65062494d735SSepherosa Ziehau 
650715516c77SSepherosa Ziehau 	/* Save capabilities for later verification. */
650815516c77SSepherosa Ziehau 	old_caps = sc->hn_caps;
650915516c77SSepherosa Ziehau 	sc->hn_caps = 0;
651015516c77SSepherosa Ziehau 
651115516c77SSepherosa Ziehau 	/* Clear RSS stuffs. */
651215516c77SSepherosa Ziehau 	sc->hn_rss_ind_size = 0;
651315516c77SSepherosa Ziehau 	sc->hn_rss_hash = 0;
6514642ec226SSepherosa Ziehau 	sc->hn_rss_hcap = 0;
651515516c77SSepherosa Ziehau 
651615516c77SSepherosa Ziehau 	/*
651715516c77SSepherosa Ziehau 	 * Attach the primary channel _before_ attaching NVS and RNDIS.
651815516c77SSepherosa Ziehau 	 */
651915516c77SSepherosa Ziehau 	error = hn_chan_attach(sc, sc->hn_prichan);
652015516c77SSepherosa Ziehau 	if (error)
652171e8ac56SSepherosa Ziehau 		goto failed;
652215516c77SSepherosa Ziehau 
652315516c77SSepherosa Ziehau 	/*
652415516c77SSepherosa Ziehau 	 * Attach NVS.
652515516c77SSepherosa Ziehau 	 */
652615516c77SSepherosa Ziehau 	error = hn_nvs_attach(sc, mtu);
652715516c77SSepherosa Ziehau 	if (error)
652871e8ac56SSepherosa Ziehau 		goto failed;
652971e8ac56SSepherosa Ziehau 	attached |= ATTACHED_NVS;
653015516c77SSepherosa Ziehau 
653115516c77SSepherosa Ziehau 	/*
653215516c77SSepherosa Ziehau 	 * Attach RNDIS _after_ NVS is attached.
653315516c77SSepherosa Ziehau 	 */
6534b3b75d9cSSepherosa Ziehau 	error = hn_rndis_attach(sc, mtu, &rndis_inited);
6535b3b75d9cSSepherosa Ziehau 	if (rndis_inited)
6536b3b75d9cSSepherosa Ziehau 		attached |= ATTACHED_RNDIS;
653715516c77SSepherosa Ziehau 	if (error)
653871e8ac56SSepherosa Ziehau 		goto failed;
653915516c77SSepherosa Ziehau 
654015516c77SSepherosa Ziehau 	/*
654115516c77SSepherosa Ziehau 	 * Make sure capabilities are not changed.
654215516c77SSepherosa Ziehau 	 */
654315516c77SSepherosa Ziehau 	if (device_is_attached(sc->hn_dev) && old_caps != sc->hn_caps) {
654415516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "caps mismatch old 0x%08x, new 0x%08x\n",
654515516c77SSepherosa Ziehau 		    old_caps, sc->hn_caps);
654671e8ac56SSepherosa Ziehau 		error = ENXIO;
654771e8ac56SSepherosa Ziehau 		goto failed;
654815516c77SSepherosa Ziehau 	}
654915516c77SSepherosa Ziehau 
655015516c77SSepherosa Ziehau 	/*
655115516c77SSepherosa Ziehau 	 * Allocate sub-channels for multi-TX/RX rings.
655215516c77SSepherosa Ziehau 	 *
655315516c77SSepherosa Ziehau 	 * NOTE:
655415516c77SSepherosa Ziehau 	 * The # of RX rings that can be used is equivalent to the # of
655515516c77SSepherosa Ziehau 	 * channels to be requested.
655615516c77SSepherosa Ziehau 	 */
655715516c77SSepherosa Ziehau 	nsubch = sc->hn_rx_ring_cnt - 1;
655815516c77SSepherosa Ziehau 	error = hn_synth_alloc_subchans(sc, &nsubch);
655915516c77SSepherosa Ziehau 	if (error)
656071e8ac56SSepherosa Ziehau 		goto failed;
656171e8ac56SSepherosa Ziehau 	/* NOTE: _Full_ synthetic parts detach is required now. */
656271e8ac56SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_SYNTH_ATTACHED;
656315516c77SSepherosa Ziehau 
656471e8ac56SSepherosa Ziehau 	/*
656571e8ac56SSepherosa Ziehau 	 * Set the # of TX/RX rings that could be used according to
656671e8ac56SSepherosa Ziehau 	 * the # of channels that NVS offered.
656771e8ac56SSepherosa Ziehau 	 */
656815516c77SSepherosa Ziehau 	nchan = nsubch + 1;
656971e8ac56SSepherosa Ziehau 	hn_set_ring_inuse(sc, nchan);
657015516c77SSepherosa Ziehau 	if (nchan == 1) {
657115516c77SSepherosa Ziehau 		/* Only the primary channel can be used; done */
657215516c77SSepherosa Ziehau 		goto back;
657315516c77SSepherosa Ziehau 	}
657415516c77SSepherosa Ziehau 
657515516c77SSepherosa Ziehau 	/*
657671e8ac56SSepherosa Ziehau 	 * Attach the sub-channels.
6577afd4971bSSepherosa Ziehau 	 *
6578afd4971bSSepherosa Ziehau 	 * NOTE: hn_set_ring_inuse() _must_ have been called.
657915516c77SSepherosa Ziehau 	 */
658071e8ac56SSepherosa Ziehau 	error = hn_attach_subchans(sc);
658171e8ac56SSepherosa Ziehau 	if (error)
658271e8ac56SSepherosa Ziehau 		goto failed;
658315516c77SSepherosa Ziehau 
658471e8ac56SSepherosa Ziehau 	/*
658571e8ac56SSepherosa Ziehau 	 * Configure RSS key and indirect table _after_ all sub-channels
658671e8ac56SSepherosa Ziehau 	 * are attached.
658771e8ac56SSepherosa Ziehau 	 */
658815516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSKEY) == 0) {
658915516c77SSepherosa Ziehau 		/*
659015516c77SSepherosa Ziehau 		 * RSS key is not set yet; set it to the default RSS key.
659115516c77SSepherosa Ziehau 		 */
659215516c77SSepherosa Ziehau 		if (bootverbose)
659315516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS key\n");
659434d68912SSepherosa Ziehau #ifdef RSS
659534d68912SSepherosa Ziehau 		rss_getkey(rss->rss_key);
659634d68912SSepherosa Ziehau #else
659715516c77SSepherosa Ziehau 		memcpy(rss->rss_key, hn_rss_key_default, sizeof(rss->rss_key));
659834d68912SSepherosa Ziehau #endif
659915516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
660015516c77SSepherosa Ziehau 	}
660115516c77SSepherosa Ziehau 
660215516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSIND) == 0) {
660315516c77SSepherosa Ziehau 		/*
660415516c77SSepherosa Ziehau 		 * RSS indirect table is not set yet; set it up in round-
660515516c77SSepherosa Ziehau 		 * robin fashion.
660615516c77SSepherosa Ziehau 		 */
660715516c77SSepherosa Ziehau 		if (bootverbose) {
660815516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS indirect "
660915516c77SSepherosa Ziehau 			    "table\n");
661015516c77SSepherosa Ziehau 		}
661134d68912SSepherosa Ziehau 		for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
661234d68912SSepherosa Ziehau 			uint32_t subidx;
661334d68912SSepherosa Ziehau 
661434d68912SSepherosa Ziehau #ifdef RSS
661534d68912SSepherosa Ziehau 			subidx = rss_get_indirection_to_bucket(i);
661634d68912SSepherosa Ziehau #else
661734d68912SSepherosa Ziehau 			subidx = i;
661834d68912SSepherosa Ziehau #endif
661934d68912SSepherosa Ziehau 			rss->rss_ind[i] = subidx % nchan;
662034d68912SSepherosa Ziehau 		}
662115516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSIND;
662215516c77SSepherosa Ziehau 	} else {
662315516c77SSepherosa Ziehau 		/*
662415516c77SSepherosa Ziehau 		 * # of usable channels may be changed, so we have to
662515516c77SSepherosa Ziehau 		 * make sure that all entries in RSS indirect table
662615516c77SSepherosa Ziehau 		 * are valid.
6627afd4971bSSepherosa Ziehau 		 *
6628afd4971bSSepherosa Ziehau 		 * NOTE: hn_set_ring_inuse() _must_ have been called.
662915516c77SSepherosa Ziehau 		 */
6630afd4971bSSepherosa Ziehau 		hn_rss_ind_fixup(sc);
663115516c77SSepherosa Ziehau 	}
663215516c77SSepherosa Ziehau 
6633642ec226SSepherosa Ziehau 	sc->hn_rss_hash = sc->hn_rss_hcap;
6634642ec226SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) ||
6635642ec226SSepherosa Ziehau 	    (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
6636642ec226SSepherosa Ziehau 		/* NOTE: Don't reconfigure RSS; will do immediately. */
6637642ec226SSepherosa Ziehau 		hn_vf_rss_fixup(sc, false);
6638642ec226SSepherosa Ziehau 	}
663915516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
664015516c77SSepherosa Ziehau 	if (error)
664171e8ac56SSepherosa Ziehau 		goto failed;
664271e8ac56SSepherosa Ziehau back:
6643dc13fee6SSepherosa Ziehau 	/*
6644dc13fee6SSepherosa Ziehau 	 * Fixup transmission aggregation setup.
6645dc13fee6SSepherosa Ziehau 	 */
6646dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
6647b3b75d9cSSepherosa Ziehau 	hn_rndis_init_fixat(sc, nchan);
664815516c77SSepherosa Ziehau 	return (0);
664971e8ac56SSepherosa Ziehau 
665071e8ac56SSepherosa Ziehau failed:
665171e8ac56SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
6652b3b75d9cSSepherosa Ziehau 		hn_rndis_init_fixat(sc, nchan);
665371e8ac56SSepherosa Ziehau 		hn_synth_detach(sc);
665471e8ac56SSepherosa Ziehau 	} else {
6655b3b75d9cSSepherosa Ziehau 		if (attached & ATTACHED_RNDIS) {
6656b3b75d9cSSepherosa Ziehau 			hn_rndis_init_fixat(sc, nchan);
665771e8ac56SSepherosa Ziehau 			hn_rndis_detach(sc);
6658b3b75d9cSSepherosa Ziehau 		}
665971e8ac56SSepherosa Ziehau 		if (attached & ATTACHED_NVS)
666071e8ac56SSepherosa Ziehau 			hn_nvs_detach(sc);
666171e8ac56SSepherosa Ziehau 		hn_chan_detach(sc, sc->hn_prichan);
666271e8ac56SSepherosa Ziehau 		/* Restore old capabilities. */
666371e8ac56SSepherosa Ziehau 		sc->hn_caps = old_caps;
666471e8ac56SSepherosa Ziehau 	}
666571e8ac56SSepherosa Ziehau 	return (error);
666671e8ac56SSepherosa Ziehau 
666771e8ac56SSepherosa Ziehau #undef ATTACHED_RNDIS
666871e8ac56SSepherosa Ziehau #undef ATTACHED_NVS
666915516c77SSepherosa Ziehau }
667015516c77SSepherosa Ziehau 
667115516c77SSepherosa Ziehau /*
667215516c77SSepherosa Ziehau  * NOTE:
667315516c77SSepherosa Ziehau  * The interface must have been suspended though hn_suspend(), before
667415516c77SSepherosa Ziehau  * this function get called.
667515516c77SSepherosa Ziehau  */
667615516c77SSepherosa Ziehau static void
667715516c77SSepherosa Ziehau hn_synth_detach(struct hn_softc *sc)
667815516c77SSepherosa Ziehau {
667915516c77SSepherosa Ziehau 
668015516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
668115516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
668215516c77SSepherosa Ziehau 
668315516c77SSepherosa Ziehau 	/* Detach the RNDIS first. */
668415516c77SSepherosa Ziehau 	hn_rndis_detach(sc);
668515516c77SSepherosa Ziehau 
668615516c77SSepherosa Ziehau 	/* Detach NVS. */
668715516c77SSepherosa Ziehau 	hn_nvs_detach(sc);
668815516c77SSepherosa Ziehau 
668915516c77SSepherosa Ziehau 	/* Detach all of the channels. */
669015516c77SSepherosa Ziehau 	hn_detach_allchans(sc);
669115516c77SSepherosa Ziehau 
6692ace5ce7eSWei Hu 	if (vmbus_current_version >= VMBUS_VERSION_WIN10 && sc->hn_rxbuf_gpadl != 0) {
6693ace5ce7eSWei Hu 		/*
6694ace5ce7eSWei Hu 		 * Host is post-Win2016, disconnect RXBUF from primary channel here.
6695ace5ce7eSWei Hu 		 */
6696ace5ce7eSWei Hu 		int error;
6697ace5ce7eSWei Hu 
6698ace5ce7eSWei Hu 		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
6699ace5ce7eSWei Hu 		    sc->hn_rxbuf_gpadl);
6700ace5ce7eSWei Hu 		if (error) {
6701ace5ce7eSWei Hu 			if_printf(sc->hn_ifp,
6702ace5ce7eSWei Hu 			    "rxbuf gpadl disconn failed: %d\n", error);
6703ace5ce7eSWei Hu 			sc->hn_flags |= HN_FLAG_RXBUF_REF;
6704ace5ce7eSWei Hu 		}
6705ace5ce7eSWei Hu 		sc->hn_rxbuf_gpadl = 0;
6706ace5ce7eSWei Hu 	}
6707ace5ce7eSWei Hu 
6708ace5ce7eSWei Hu 	if (vmbus_current_version >= VMBUS_VERSION_WIN10 && sc->hn_chim_gpadl != 0) {
6709ace5ce7eSWei Hu 		/*
6710ace5ce7eSWei Hu 		 * Host is post-Win2016, disconnect chimney sending buffer from
6711ace5ce7eSWei Hu 		 * primary channel here.
6712ace5ce7eSWei Hu 		 */
6713ace5ce7eSWei Hu 		int error;
6714ace5ce7eSWei Hu 
6715ace5ce7eSWei Hu 		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
6716ace5ce7eSWei Hu 		    sc->hn_chim_gpadl);
6717ace5ce7eSWei Hu 		if (error) {
6718ace5ce7eSWei Hu 			if_printf(sc->hn_ifp,
6719ace5ce7eSWei Hu 			    "chim gpadl disconn failed: %d\n", error);
6720ace5ce7eSWei Hu 			sc->hn_flags |= HN_FLAG_CHIM_REF;
6721ace5ce7eSWei Hu 		}
6722ace5ce7eSWei Hu 		sc->hn_chim_gpadl = 0;
6723ace5ce7eSWei Hu 	}
672415516c77SSepherosa Ziehau 	sc->hn_flags &= ~HN_FLAG_SYNTH_ATTACHED;
672515516c77SSepherosa Ziehau }
672615516c77SSepherosa Ziehau 
672715516c77SSepherosa Ziehau static void
672815516c77SSepherosa Ziehau hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt)
672915516c77SSepherosa Ziehau {
673015516c77SSepherosa Ziehau 	KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_cnt,
673115516c77SSepherosa Ziehau 	    ("invalid ring count %d", ring_cnt));
673215516c77SSepherosa Ziehau 
673315516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt > ring_cnt)
673415516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = ring_cnt;
673515516c77SSepherosa Ziehau 	else
673615516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
673715516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = ring_cnt;
673815516c77SSepherosa Ziehau 
673934d68912SSepherosa Ziehau #ifdef RSS
674034d68912SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse != rss_getnumbuckets()) {
674134d68912SSepherosa Ziehau 		if_printf(sc->hn_ifp, "# of RX rings (%d) does not match "
674234d68912SSepherosa Ziehau 		    "# of RSS buckets (%d)\n", sc->hn_rx_ring_inuse,
674334d68912SSepherosa Ziehau 		    rss_getnumbuckets());
674434d68912SSepherosa Ziehau 	}
674534d68912SSepherosa Ziehau #endif
674634d68912SSepherosa Ziehau 
674715516c77SSepherosa Ziehau 	if (bootverbose) {
674815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n",
674915516c77SSepherosa Ziehau 		    sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
675015516c77SSepherosa Ziehau 	}
675115516c77SSepherosa Ziehau }
675215516c77SSepherosa Ziehau 
675315516c77SSepherosa Ziehau static void
675425641fc7SSepherosa Ziehau hn_chan_drain(struct hn_softc *sc, struct vmbus_channel *chan)
675515516c77SSepherosa Ziehau {
675615516c77SSepherosa Ziehau 
675725641fc7SSepherosa Ziehau 	/*
675825641fc7SSepherosa Ziehau 	 * NOTE:
675925641fc7SSepherosa Ziehau 	 * The TX bufring will not be drained by the hypervisor,
676025641fc7SSepherosa Ziehau 	 * if the primary channel is revoked.
676125641fc7SSepherosa Ziehau 	 */
676225641fc7SSepherosa Ziehau 	while (!vmbus_chan_rx_empty(chan) ||
676325641fc7SSepherosa Ziehau 	    (!vmbus_chan_is_revoked(sc->hn_prichan) &&
676425641fc7SSepherosa Ziehau 	     !vmbus_chan_tx_empty(chan)))
676515516c77SSepherosa Ziehau 		pause("waitch", 1);
676615516c77SSepherosa Ziehau 	vmbus_chan_intr_drain(chan);
676715516c77SSepherosa Ziehau }
676815516c77SSepherosa Ziehau 
676915516c77SSepherosa Ziehau static void
6770b3b75d9cSSepherosa Ziehau hn_disable_rx(struct hn_softc *sc)
6771b3b75d9cSSepherosa Ziehau {
6772b3b75d9cSSepherosa Ziehau 
6773b3b75d9cSSepherosa Ziehau 	/*
6774b3b75d9cSSepherosa Ziehau 	 * Disable RX by clearing RX filter forcefully.
6775b3b75d9cSSepherosa Ziehau 	 */
6776b3b75d9cSSepherosa Ziehau 	sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE;
6777b3b75d9cSSepherosa Ziehau 	hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); /* ignore error */
6778b3b75d9cSSepherosa Ziehau 
6779b3b75d9cSSepherosa Ziehau 	/*
6780b3b75d9cSSepherosa Ziehau 	 * Give RNDIS enough time to flush all pending data packets.
6781b3b75d9cSSepherosa Ziehau 	 */
6782b3b75d9cSSepherosa Ziehau 	pause("waitrx", (200 * hz) / 1000);
6783b3b75d9cSSepherosa Ziehau }
6784b3b75d9cSSepherosa Ziehau 
6785b3b75d9cSSepherosa Ziehau /*
6786b3b75d9cSSepherosa Ziehau  * NOTE:
6787b3b75d9cSSepherosa Ziehau  * RX/TX _must_ have been suspended/disabled, before this function
6788b3b75d9cSSepherosa Ziehau  * is called.
6789b3b75d9cSSepherosa Ziehau  */
6790b3b75d9cSSepherosa Ziehau static void
6791b3b75d9cSSepherosa Ziehau hn_drain_rxtx(struct hn_softc *sc, int nchan)
679215516c77SSepherosa Ziehau {
679315516c77SSepherosa Ziehau 	struct vmbus_channel **subch = NULL;
6794b3b75d9cSSepherosa Ziehau 	int nsubch;
6795b3b75d9cSSepherosa Ziehau 
6796b3b75d9cSSepherosa Ziehau 	/*
6797b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX bufrings and interrupts.
6798b3b75d9cSSepherosa Ziehau 	 */
6799b3b75d9cSSepherosa Ziehau 	nsubch = nchan - 1;
6800b3b75d9cSSepherosa Ziehau 	if (nsubch > 0)
6801b3b75d9cSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
6802b3b75d9cSSepherosa Ziehau 
6803b3b75d9cSSepherosa Ziehau 	if (subch != NULL) {
6804b3b75d9cSSepherosa Ziehau 		int i;
6805b3b75d9cSSepherosa Ziehau 
6806b3b75d9cSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
6807b3b75d9cSSepherosa Ziehau 			hn_chan_drain(sc, subch[i]);
6808b3b75d9cSSepherosa Ziehau 	}
6809b3b75d9cSSepherosa Ziehau 	hn_chan_drain(sc, sc->hn_prichan);
6810b3b75d9cSSepherosa Ziehau 
6811b3b75d9cSSepherosa Ziehau 	if (subch != NULL)
6812b3b75d9cSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
6813b3b75d9cSSepherosa Ziehau }
6814b3b75d9cSSepherosa Ziehau 
6815b3b75d9cSSepherosa Ziehau static void
6816b3b75d9cSSepherosa Ziehau hn_suspend_data(struct hn_softc *sc)
6817b3b75d9cSSepherosa Ziehau {
681825641fc7SSepherosa Ziehau 	struct hn_tx_ring *txr;
6819b3b75d9cSSepherosa Ziehau 	int i;
682015516c77SSepherosa Ziehau 
682115516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
682215516c77SSepherosa Ziehau 
682315516c77SSepherosa Ziehau 	/*
682415516c77SSepherosa Ziehau 	 * Suspend TX.
682515516c77SSepherosa Ziehau 	 */
682615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
682725641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
682815516c77SSepherosa Ziehau 
682915516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
683015516c77SSepherosa Ziehau 		txr->hn_suspended = 1;
683115516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
683215516c77SSepherosa Ziehau 		/* No one is able send more packets now. */
683315516c77SSepherosa Ziehau 
683425641fc7SSepherosa Ziehau 		/*
683525641fc7SSepherosa Ziehau 		 * Wait for all pending sends to finish.
683625641fc7SSepherosa Ziehau 		 *
683725641fc7SSepherosa Ziehau 		 * NOTE:
683825641fc7SSepherosa Ziehau 		 * We will _not_ receive all pending send-done, if the
683925641fc7SSepherosa Ziehau 		 * primary channel is revoked.
684025641fc7SSepherosa Ziehau 		 */
684125641fc7SSepherosa Ziehau 		while (hn_tx_ring_pending(txr) &&
684225641fc7SSepherosa Ziehau 		    !vmbus_chan_is_revoked(sc->hn_prichan))
684315516c77SSepherosa Ziehau 			pause("hnwtx", 1 /* 1 tick */);
684415516c77SSepherosa Ziehau 	}
684515516c77SSepherosa Ziehau 
684615516c77SSepherosa Ziehau 	/*
6847b3b75d9cSSepherosa Ziehau 	 * Disable RX.
684815516c77SSepherosa Ziehau 	 */
6849b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
685015516c77SSepherosa Ziehau 
685115516c77SSepherosa Ziehau 	/*
6852b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX.
685315516c77SSepherosa Ziehau 	 */
6854b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, sc->hn_rx_ring_inuse);
685525641fc7SSepherosa Ziehau 
685625641fc7SSepherosa Ziehau 	/*
685725641fc7SSepherosa Ziehau 	 * Drain any pending TX tasks.
685825641fc7SSepherosa Ziehau 	 *
685925641fc7SSepherosa Ziehau 	 * NOTE:
6860b3b75d9cSSepherosa Ziehau 	 * The above hn_drain_rxtx() can dispatch TX tasks, so the TX
6861b3b75d9cSSepherosa Ziehau 	 * tasks will have to be drained _after_ the above hn_drain_rxtx().
686225641fc7SSepherosa Ziehau 	 */
686325641fc7SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
686425641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
686525641fc7SSepherosa Ziehau 
686625641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task);
686725641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task);
686825641fc7SSepherosa Ziehau 	}
686915516c77SSepherosa Ziehau }
687015516c77SSepherosa Ziehau 
687115516c77SSepherosa Ziehau static void
687215516c77SSepherosa Ziehau hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused)
687315516c77SSepherosa Ziehau {
687415516c77SSepherosa Ziehau 
687515516c77SSepherosa Ziehau 	((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL;
687615516c77SSepherosa Ziehau }
687715516c77SSepherosa Ziehau 
687815516c77SSepherosa Ziehau static void
687915516c77SSepherosa Ziehau hn_suspend_mgmt(struct hn_softc *sc)
688015516c77SSepherosa Ziehau {
688115516c77SSepherosa Ziehau 	struct task task;
688215516c77SSepherosa Ziehau 
688315516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
688415516c77SSepherosa Ziehau 
688515516c77SSepherosa Ziehau 	/*
688615516c77SSepherosa Ziehau 	 * Make sure that hn_mgmt_taskq0 can nolonger be accessed
688715516c77SSepherosa Ziehau 	 * through hn_mgmt_taskq.
688815516c77SSepherosa Ziehau 	 */
688915516c77SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_suspend_mgmt_taskfunc, sc);
689015516c77SSepherosa Ziehau 	vmbus_chan_run_task(sc->hn_prichan, &task);
689115516c77SSepherosa Ziehau 
689215516c77SSepherosa Ziehau 	/*
689315516c77SSepherosa Ziehau 	 * Make sure that all pending management tasks are completed.
689415516c77SSepherosa Ziehau 	 */
689515516c77SSepherosa Ziehau 	taskqueue_drain(sc->hn_mgmt_taskq0, &sc->hn_netchg_init);
689615516c77SSepherosa Ziehau 	taskqueue_drain_timeout(sc->hn_mgmt_taskq0, &sc->hn_netchg_status);
689715516c77SSepherosa Ziehau 	taskqueue_drain_all(sc->hn_mgmt_taskq0);
689815516c77SSepherosa Ziehau }
689915516c77SSepherosa Ziehau 
690015516c77SSepherosa Ziehau static void
690115516c77SSepherosa Ziehau hn_suspend(struct hn_softc *sc)
690215516c77SSepherosa Ziehau {
690315516c77SSepherosa Ziehau 
690487f8129dSSepherosa Ziehau 	/* Disable polling. */
690587f8129dSSepherosa Ziehau 	hn_polling(sc, 0);
690687f8129dSSepherosa Ziehau 
69079c6cae24SSepherosa Ziehau 	/*
69089c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
69099c6cae24SSepherosa Ziehau 	 * device is receiving packets, so the data path of the
69109c6cae24SSepherosa Ziehau 	 * synthetic device must be suspended.
69119c6cae24SSepherosa Ziehau 	 */
69125bdfd3fdSDexuan Cui 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
6913962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
691415516c77SSepherosa Ziehau 		hn_suspend_data(sc);
691515516c77SSepherosa Ziehau 	hn_suspend_mgmt(sc);
691615516c77SSepherosa Ziehau }
691715516c77SSepherosa Ziehau 
691815516c77SSepherosa Ziehau static void
691915516c77SSepherosa Ziehau hn_resume_tx(struct hn_softc *sc, int tx_ring_cnt)
692015516c77SSepherosa Ziehau {
692115516c77SSepherosa Ziehau 	int i;
692215516c77SSepherosa Ziehau 
692315516c77SSepherosa Ziehau 	KASSERT(tx_ring_cnt <= sc->hn_tx_ring_cnt,
692415516c77SSepherosa Ziehau 	    ("invalid TX ring count %d", tx_ring_cnt));
692515516c77SSepherosa Ziehau 
692615516c77SSepherosa Ziehau 	for (i = 0; i < tx_ring_cnt; ++i) {
692715516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
692815516c77SSepherosa Ziehau 
692915516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
693015516c77SSepherosa Ziehau 		txr->hn_suspended = 0;
693115516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
693215516c77SSepherosa Ziehau 	}
693315516c77SSepherosa Ziehau }
693415516c77SSepherosa Ziehau 
693515516c77SSepherosa Ziehau static void
693615516c77SSepherosa Ziehau hn_resume_data(struct hn_softc *sc)
693715516c77SSepherosa Ziehau {
693815516c77SSepherosa Ziehau 	int i;
693915516c77SSepherosa Ziehau 
694015516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
694115516c77SSepherosa Ziehau 
694215516c77SSepherosa Ziehau 	/*
694315516c77SSepherosa Ziehau 	 * Re-enable RX.
694415516c77SSepherosa Ziehau 	 */
6945c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
694615516c77SSepherosa Ziehau 
694715516c77SSepherosa Ziehau 	/*
694815516c77SSepherosa Ziehau 	 * Make sure to clear suspend status on "all" TX rings,
694915516c77SSepherosa Ziehau 	 * since hn_tx_ring_inuse can be changed after
695015516c77SSepherosa Ziehau 	 * hn_suspend_data().
695115516c77SSepherosa Ziehau 	 */
695215516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_cnt);
695315516c77SSepherosa Ziehau 
695423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
695523bf9e15SSepherosa Ziehau 	if (!hn_use_if_start)
695623bf9e15SSepherosa Ziehau #endif
695723bf9e15SSepherosa Ziehau 	{
695815516c77SSepherosa Ziehau 		/*
695915516c77SSepherosa Ziehau 		 * Flush unused drbrs, since hn_tx_ring_inuse may be
696015516c77SSepherosa Ziehau 		 * reduced.
696115516c77SSepherosa Ziehau 		 */
696215516c77SSepherosa Ziehau 		for (i = sc->hn_tx_ring_inuse; i < sc->hn_tx_ring_cnt; ++i)
696315516c77SSepherosa Ziehau 			hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
696415516c77SSepherosa Ziehau 	}
696515516c77SSepherosa Ziehau 
696615516c77SSepherosa Ziehau 	/*
696715516c77SSepherosa Ziehau 	 * Kick start TX.
696815516c77SSepherosa Ziehau 	 */
696915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
697015516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
697115516c77SSepherosa Ziehau 
697215516c77SSepherosa Ziehau 		/*
697315516c77SSepherosa Ziehau 		 * Use txeof task, so that any pending oactive can be
697415516c77SSepherosa Ziehau 		 * cleared properly.
697515516c77SSepherosa Ziehau 		 */
697615516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
697715516c77SSepherosa Ziehau 	}
697815516c77SSepherosa Ziehau }
697915516c77SSepherosa Ziehau 
698015516c77SSepherosa Ziehau static void
698115516c77SSepherosa Ziehau hn_resume_mgmt(struct hn_softc *sc)
698215516c77SSepherosa Ziehau {
698315516c77SSepherosa Ziehau 
698415516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
698515516c77SSepherosa Ziehau 
698615516c77SSepherosa Ziehau 	/*
698715516c77SSepherosa Ziehau 	 * Kick off network change detection, if it was pending.
698815516c77SSepherosa Ziehau 	 * If no network change was pending, start link status
698915516c77SSepherosa Ziehau 	 * checks, which is more lightweight than network change
699015516c77SSepherosa Ziehau 	 * detection.
699115516c77SSepherosa Ziehau 	 */
699215516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
699315516c77SSepherosa Ziehau 		hn_change_network(sc);
699415516c77SSepherosa Ziehau 	else
699515516c77SSepherosa Ziehau 		hn_update_link_status(sc);
699615516c77SSepherosa Ziehau }
699715516c77SSepherosa Ziehau 
699815516c77SSepherosa Ziehau static void
699915516c77SSepherosa Ziehau hn_resume(struct hn_softc *sc)
700015516c77SSepherosa Ziehau {
700115516c77SSepherosa Ziehau 
70029c6cae24SSepherosa Ziehau 	/*
70039c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
70049c6cae24SSepherosa Ziehau 	 * device have to receive packets, so the data path of the
70059c6cae24SSepherosa Ziehau 	 * synthetic device must be resumed.
70069c6cae24SSepherosa Ziehau 	 */
70075bdfd3fdSDexuan Cui 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
7008962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
700915516c77SSepherosa Ziehau 		hn_resume_data(sc);
70105bdfd3fdSDexuan Cui 
70115bdfd3fdSDexuan Cui 	/*
70129c6cae24SSepherosa Ziehau 	 * Don't resume link status change if VF is attached/activated.
70139c6cae24SSepherosa Ziehau 	 * - In the non-transparent VF mode, the synthetic device marks
70149c6cae24SSepherosa Ziehau 	 *   link down until the VF is deactivated; i.e. VF is down.
70159c6cae24SSepherosa Ziehau 	 * - In transparent VF mode, VF's media status is used until
70169c6cae24SSepherosa Ziehau 	 *   the VF is detached.
70175bdfd3fdSDexuan Cui 	 */
70189c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) == 0 &&
70199c6cae24SSepherosa Ziehau 	    !(hn_xpnt_vf && sc->hn_vf_ifp != NULL))
702015516c77SSepherosa Ziehau 		hn_resume_mgmt(sc);
702187f8129dSSepherosa Ziehau 
702287f8129dSSepherosa Ziehau 	/*
702387f8129dSSepherosa Ziehau 	 * Re-enable polling if this interface is running and
702487f8129dSSepherosa Ziehau 	 * the polling is requested.
702587f8129dSSepherosa Ziehau 	 */
702687f8129dSSepherosa Ziehau 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->hn_pollhz > 0)
702787f8129dSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
702815516c77SSepherosa Ziehau }
702915516c77SSepherosa Ziehau 
703015516c77SSepherosa Ziehau static void
703115516c77SSepherosa Ziehau hn_rndis_rx_status(struct hn_softc *sc, const void *data, int dlen)
703215516c77SSepherosa Ziehau {
703315516c77SSepherosa Ziehau 	const struct rndis_status_msg *msg;
703415516c77SSepherosa Ziehau 	int ofs;
703515516c77SSepherosa Ziehau 
703615516c77SSepherosa Ziehau 	if (dlen < sizeof(*msg)) {
703715516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid RNDIS status\n");
703815516c77SSepherosa Ziehau 		return;
703915516c77SSepherosa Ziehau 	}
704015516c77SSepherosa Ziehau 	msg = data;
704115516c77SSepherosa Ziehau 
704215516c77SSepherosa Ziehau 	switch (msg->rm_status) {
704315516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_CONNECT:
704415516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_DISCONNECT:
704515516c77SSepherosa Ziehau 		hn_update_link_status(sc);
704615516c77SSepherosa Ziehau 		break;
704715516c77SSepherosa Ziehau 
704815516c77SSepherosa Ziehau 	case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
704940905afaSSepherosa Ziehau 	case RNDIS_STATUS_LINK_SPEED_CHANGE:
705015516c77SSepherosa Ziehau 		/* Not really useful; ignore. */
705115516c77SSepherosa Ziehau 		break;
705215516c77SSepherosa Ziehau 
705315516c77SSepherosa Ziehau 	case RNDIS_STATUS_NETWORK_CHANGE:
705415516c77SSepherosa Ziehau 		ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
705515516c77SSepherosa Ziehau 		if (dlen < ofs + msg->rm_stbuflen ||
705615516c77SSepherosa Ziehau 		    msg->rm_stbuflen < sizeof(uint32_t)) {
705715516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed\n");
705815516c77SSepherosa Ziehau 		} else {
705915516c77SSepherosa Ziehau 			uint32_t change;
706015516c77SSepherosa Ziehau 
706115516c77SSepherosa Ziehau 			memcpy(&change, ((const uint8_t *)msg) + ofs,
706215516c77SSepherosa Ziehau 			    sizeof(change));
706315516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed, change %u\n",
706415516c77SSepherosa Ziehau 			    change);
706515516c77SSepherosa Ziehau 		}
706615516c77SSepherosa Ziehau 		hn_change_network(sc);
706715516c77SSepherosa Ziehau 		break;
706815516c77SSepherosa Ziehau 
706915516c77SSepherosa Ziehau 	default:
707015516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
707115516c77SSepherosa Ziehau 		    msg->rm_status);
707215516c77SSepherosa Ziehau 		break;
707315516c77SSepherosa Ziehau 	}
707415516c77SSepherosa Ziehau }
707515516c77SSepherosa Ziehau 
707615516c77SSepherosa Ziehau static int
707715516c77SSepherosa Ziehau hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info)
707815516c77SSepherosa Ziehau {
707915516c77SSepherosa Ziehau 	const struct rndis_pktinfo *pi = info_data;
708015516c77SSepherosa Ziehau 	uint32_t mask = 0;
708115516c77SSepherosa Ziehau 
708215516c77SSepherosa Ziehau 	while (info_dlen != 0) {
708315516c77SSepherosa Ziehau 		const void *data;
708415516c77SSepherosa Ziehau 		uint32_t dlen;
708515516c77SSepherosa Ziehau 
708615516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < sizeof(*pi)))
708715516c77SSepherosa Ziehau 			return (EINVAL);
708815516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < pi->rm_size))
708915516c77SSepherosa Ziehau 			return (EINVAL);
709015516c77SSepherosa Ziehau 		info_dlen -= pi->rm_size;
709115516c77SSepherosa Ziehau 
709215516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
709315516c77SSepherosa Ziehau 			return (EINVAL);
709415516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
709515516c77SSepherosa Ziehau 			return (EINVAL);
709615516c77SSepherosa Ziehau 		dlen = pi->rm_size - pi->rm_pktinfooffset;
709715516c77SSepherosa Ziehau 		data = pi->rm_data;
709815516c77SSepherosa Ziehau 
7099a491581fSWei Hu 		if (pi->rm_internal == 1) {
7100a491581fSWei Hu 			switch (pi->rm_type) {
7101a491581fSWei Hu 			case NDIS_PKTINFO_IT_PKTINFO_ID:
7102a491581fSWei Hu 				if (__predict_false(dlen < NDIS_PKTINFOID_SZ))
7103a491581fSWei Hu 					return (EINVAL);
7104a491581fSWei Hu 				info->pktinfo_id =
7105a491581fSWei Hu 				    (const struct packet_info_id *)data;
7106a491581fSWei Hu 				mask |= HN_RXINFO_PKTINFO_ID;
7107a491581fSWei Hu 				break;
7108a491581fSWei Hu 
7109a491581fSWei Hu 			default:
7110a491581fSWei Hu 				goto next;
7111a491581fSWei Hu 			}
7112a491581fSWei Hu 		} else {
711315516c77SSepherosa Ziehau 			switch (pi->rm_type) {
711415516c77SSepherosa Ziehau 			case NDIS_PKTINFO_TYPE_VLAN:
7115a491581fSWei Hu 				if (__predict_false(dlen
7116a491581fSWei Hu 				    < NDIS_VLAN_INFO_SIZE))
711715516c77SSepherosa Ziehau 					return (EINVAL);
7118a491581fSWei Hu 				info->vlan_info = (const uint32_t *)data;
711915516c77SSepherosa Ziehau 				mask |= HN_RXINFO_VLAN;
712015516c77SSepherosa Ziehau 				break;
712115516c77SSepherosa Ziehau 
712215516c77SSepherosa Ziehau 			case NDIS_PKTINFO_TYPE_CSUM:
7123a491581fSWei Hu 				if (__predict_false(dlen
7124a491581fSWei Hu 				    < NDIS_RXCSUM_INFO_SIZE))
712515516c77SSepherosa Ziehau 					return (EINVAL);
7126a491581fSWei Hu 				info->csum_info = (const uint32_t *)data;
712715516c77SSepherosa Ziehau 				mask |= HN_RXINFO_CSUM;
712815516c77SSepherosa Ziehau 				break;
712915516c77SSepherosa Ziehau 
713015516c77SSepherosa Ziehau 			case HN_NDIS_PKTINFO_TYPE_HASHVAL:
7131a491581fSWei Hu 				if (__predict_false(dlen
7132a491581fSWei Hu 				    < HN_NDIS_HASH_VALUE_SIZE))
713315516c77SSepherosa Ziehau 					return (EINVAL);
7134a491581fSWei Hu 				info->hash_value = (const uint32_t *)data;
713515516c77SSepherosa Ziehau 				mask |= HN_RXINFO_HASHVAL;
713615516c77SSepherosa Ziehau 				break;
713715516c77SSepherosa Ziehau 
713815516c77SSepherosa Ziehau 			case HN_NDIS_PKTINFO_TYPE_HASHINF:
7139a491581fSWei Hu 				if (__predict_false(dlen
7140a491581fSWei Hu 				    < HN_NDIS_HASH_INFO_SIZE))
714115516c77SSepherosa Ziehau 					return (EINVAL);
7142a491581fSWei Hu 				info->hash_info = (const uint32_t *)data;
714315516c77SSepherosa Ziehau 				mask |= HN_RXINFO_HASHINF;
714415516c77SSepherosa Ziehau 				break;
714515516c77SSepherosa Ziehau 
714615516c77SSepherosa Ziehau 			default:
714715516c77SSepherosa Ziehau 				goto next;
714815516c77SSepherosa Ziehau 			}
7149a491581fSWei Hu 		}
715015516c77SSepherosa Ziehau 
715115516c77SSepherosa Ziehau 		if (mask == HN_RXINFO_ALL) {
715215516c77SSepherosa Ziehau 			/* All found; done */
715315516c77SSepherosa Ziehau 			break;
715415516c77SSepherosa Ziehau 		}
715515516c77SSepherosa Ziehau next:
715615516c77SSepherosa Ziehau 		pi = (const struct rndis_pktinfo *)
715715516c77SSepherosa Ziehau 		    ((const uint8_t *)pi + pi->rm_size);
715815516c77SSepherosa Ziehau 	}
715915516c77SSepherosa Ziehau 
716015516c77SSepherosa Ziehau 	/*
716115516c77SSepherosa Ziehau 	 * Final fixup.
716215516c77SSepherosa Ziehau 	 * - If there is no hash value, invalidate the hash info.
716315516c77SSepherosa Ziehau 	 */
716415516c77SSepherosa Ziehau 	if ((mask & HN_RXINFO_HASHVAL) == 0)
7165a491581fSWei Hu 		info->hash_info = NULL;
716615516c77SSepherosa Ziehau 	return (0);
716715516c77SSepherosa Ziehau }
716815516c77SSepherosa Ziehau 
716915516c77SSepherosa Ziehau static __inline bool
717015516c77SSepherosa Ziehau hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
717115516c77SSepherosa Ziehau {
717215516c77SSepherosa Ziehau 
717315516c77SSepherosa Ziehau 	if (off < check_off) {
717415516c77SSepherosa Ziehau 		if (__predict_true(off + len <= check_off))
717515516c77SSepherosa Ziehau 			return (false);
717615516c77SSepherosa Ziehau 	} else if (off > check_off) {
717715516c77SSepherosa Ziehau 		if (__predict_true(check_off + check_len <= off))
717815516c77SSepherosa Ziehau 			return (false);
717915516c77SSepherosa Ziehau 	}
718015516c77SSepherosa Ziehau 	return (true);
718115516c77SSepherosa Ziehau }
718215516c77SSepherosa Ziehau 
7183a491581fSWei Hu static __inline void
7184a491581fSWei Hu hn_rsc_add_data(struct hn_rx_ring *rxr, const void *data,
7185a491581fSWei Hu 		uint32_t len, struct hn_rxinfo *info)
7186a491581fSWei Hu {
7187a491581fSWei Hu 	uint32_t cnt = rxr->rsc.cnt;
7188a491581fSWei Hu 
7189a491581fSWei Hu 	if (cnt) {
7190a491581fSWei Hu 		rxr->rsc.pktlen += len;
7191a491581fSWei Hu 	} else {
7192a491581fSWei Hu 		rxr->rsc.vlan_info = info->vlan_info;
7193a491581fSWei Hu 		rxr->rsc.csum_info = info->csum_info;
7194a491581fSWei Hu 		rxr->rsc.hash_info = info->hash_info;
7195a491581fSWei Hu 		rxr->rsc.hash_value = info->hash_value;
7196a491581fSWei Hu 		rxr->rsc.pktlen = len;
7197a491581fSWei Hu 	}
7198a491581fSWei Hu 
7199a491581fSWei Hu 	rxr->rsc.frag_data[cnt] = data;
7200a491581fSWei Hu 	rxr->rsc.frag_len[cnt] = len;
7201a491581fSWei Hu 	rxr->rsc.cnt++;
7202a491581fSWei Hu }
7203a491581fSWei Hu 
720415516c77SSepherosa Ziehau static void
720515516c77SSepherosa Ziehau hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen)
720615516c77SSepherosa Ziehau {
720715516c77SSepherosa Ziehau 	const struct rndis_packet_msg *pkt;
720815516c77SSepherosa Ziehau 	struct hn_rxinfo info;
720915516c77SSepherosa Ziehau 	int data_off, pktinfo_off, data_len, pktinfo_len;
7210a491581fSWei Hu 	bool rsc_more= false;
721115516c77SSepherosa Ziehau 
721215516c77SSepherosa Ziehau 	/*
721315516c77SSepherosa Ziehau 	 * Check length.
721415516c77SSepherosa Ziehau 	 */
721515516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*pkt))) {
721615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
721715516c77SSepherosa Ziehau 		return;
721815516c77SSepherosa Ziehau 	}
721915516c77SSepherosa Ziehau 	pkt = data;
722015516c77SSepherosa Ziehau 
722115516c77SSepherosa Ziehau 	if (__predict_false(dlen < pkt->rm_len)) {
722215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
722315516c77SSepherosa Ziehau 		    "dlen %d, msglen %u\n", dlen, pkt->rm_len);
722415516c77SSepherosa Ziehau 		return;
722515516c77SSepherosa Ziehau 	}
722615516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_len <
722715516c77SSepherosa Ziehau 	    pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
722815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
722915516c77SSepherosa Ziehau 		    "msglen %u, data %u, oob %u, pktinfo %u\n",
723015516c77SSepherosa Ziehau 		    pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
723115516c77SSepherosa Ziehau 		    pkt->rm_pktinfolen);
723215516c77SSepherosa Ziehau 		return;
723315516c77SSepherosa Ziehau 	}
723415516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_datalen == 0)) {
723515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
723615516c77SSepherosa Ziehau 		return;
723715516c77SSepherosa Ziehau 	}
723815516c77SSepherosa Ziehau 
723915516c77SSepherosa Ziehau 	/*
724015516c77SSepherosa Ziehau 	 * Check offests.
724115516c77SSepherosa Ziehau 	 */
724215516c77SSepherosa Ziehau #define IS_OFFSET_INVALID(ofs)			\
724315516c77SSepherosa Ziehau 	((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN ||	\
724415516c77SSepherosa Ziehau 	 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
724515516c77SSepherosa Ziehau 
724615516c77SSepherosa Ziehau 	/* XXX Hyper-V does not meet data offset alignment requirement */
724715516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
724815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
724915516c77SSepherosa Ziehau 		    "data offset %u\n", pkt->rm_dataoffset);
725015516c77SSepherosa Ziehau 		return;
725115516c77SSepherosa Ziehau 	}
725215516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdataoffset > 0 &&
725315516c77SSepherosa Ziehau 	    IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
725415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
725515516c77SSepherosa Ziehau 		    "oob offset %u\n", pkt->rm_oobdataoffset);
725615516c77SSepherosa Ziehau 		return;
725715516c77SSepherosa Ziehau 	}
725815516c77SSepherosa Ziehau 	if (__predict_true(pkt->rm_pktinfooffset > 0) &&
725915516c77SSepherosa Ziehau 	    __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
726015516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
726115516c77SSepherosa Ziehau 		    "pktinfo offset %u\n", pkt->rm_pktinfooffset);
726215516c77SSepherosa Ziehau 		return;
726315516c77SSepherosa Ziehau 	}
726415516c77SSepherosa Ziehau 
726515516c77SSepherosa Ziehau #undef IS_OFFSET_INVALID
726615516c77SSepherosa Ziehau 
726715516c77SSepherosa Ziehau 	data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
726815516c77SSepherosa Ziehau 	data_len = pkt->rm_datalen;
726915516c77SSepherosa Ziehau 	pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
727015516c77SSepherosa Ziehau 	pktinfo_len = pkt->rm_pktinfolen;
727115516c77SSepherosa Ziehau 
727215516c77SSepherosa Ziehau 	/*
727315516c77SSepherosa Ziehau 	 * Check OOB coverage.
727415516c77SSepherosa Ziehau 	 */
727515516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdatalen != 0)) {
727615516c77SSepherosa Ziehau 		int oob_off, oob_len;
727715516c77SSepherosa Ziehau 
727815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "got oobdata\n");
727915516c77SSepherosa Ziehau 		oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
728015516c77SSepherosa Ziehau 		oob_len = pkt->rm_oobdatalen;
728115516c77SSepherosa Ziehau 
728215516c77SSepherosa Ziehau 		if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
728315516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
728415516c77SSepherosa Ziehau 			    "oob overflow, msglen %u, oob abs %d len %d\n",
728515516c77SSepherosa Ziehau 			    pkt->rm_len, oob_off, oob_len);
728615516c77SSepherosa Ziehau 			return;
728715516c77SSepherosa Ziehau 		}
728815516c77SSepherosa Ziehau 
728915516c77SSepherosa Ziehau 		/*
729015516c77SSepherosa Ziehau 		 * Check against data.
729115516c77SSepherosa Ziehau 		 */
729215516c77SSepherosa Ziehau 		if (hn_rndis_check_overlap(oob_off, oob_len,
729315516c77SSepherosa Ziehau 		    data_off, data_len)) {
729415516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
729515516c77SSepherosa Ziehau 			    "oob overlaps data, oob abs %d len %d, "
729615516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
729715516c77SSepherosa Ziehau 			    oob_off, oob_len, data_off, data_len);
729815516c77SSepherosa Ziehau 			return;
729915516c77SSepherosa Ziehau 		}
730015516c77SSepherosa Ziehau 
730115516c77SSepherosa Ziehau 		/*
730215516c77SSepherosa Ziehau 		 * Check against pktinfo.
730315516c77SSepherosa Ziehau 		 */
730415516c77SSepherosa Ziehau 		if (pktinfo_len != 0 &&
730515516c77SSepherosa Ziehau 		    hn_rndis_check_overlap(oob_off, oob_len,
730615516c77SSepherosa Ziehau 		    pktinfo_off, pktinfo_len)) {
730715516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
730815516c77SSepherosa Ziehau 			    "oob overlaps pktinfo, oob abs %d len %d, "
730915516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
731015516c77SSepherosa Ziehau 			    oob_off, oob_len, pktinfo_off, pktinfo_len);
731115516c77SSepherosa Ziehau 			return;
731215516c77SSepherosa Ziehau 		}
731315516c77SSepherosa Ziehau 	}
731415516c77SSepherosa Ziehau 
731515516c77SSepherosa Ziehau 	/*
731615516c77SSepherosa Ziehau 	 * Check per-packet-info coverage and find useful per-packet-info.
731715516c77SSepherosa Ziehau 	 */
7318a491581fSWei Hu 	info.vlan_info = NULL;
7319a491581fSWei Hu 	info.csum_info = NULL;
7320a491581fSWei Hu 	info.hash_info = NULL;
7321a491581fSWei Hu 	info.pktinfo_id = NULL;
7322a491581fSWei Hu 
732315516c77SSepherosa Ziehau 	if (__predict_true(pktinfo_len != 0)) {
732415516c77SSepherosa Ziehau 		bool overlap;
732515516c77SSepherosa Ziehau 		int error;
732615516c77SSepherosa Ziehau 
732715516c77SSepherosa Ziehau 		if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
732815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
732915516c77SSepherosa Ziehau 			    "pktinfo overflow, msglen %u, "
733015516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
733115516c77SSepherosa Ziehau 			    pkt->rm_len, pktinfo_off, pktinfo_len);
733215516c77SSepherosa Ziehau 			return;
733315516c77SSepherosa Ziehau 		}
733415516c77SSepherosa Ziehau 
733515516c77SSepherosa Ziehau 		/*
733615516c77SSepherosa Ziehau 		 * Check packet info coverage.
733715516c77SSepherosa Ziehau 		 */
733815516c77SSepherosa Ziehau 		overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
733915516c77SSepherosa Ziehau 		    data_off, data_len);
734015516c77SSepherosa Ziehau 		if (__predict_false(overlap)) {
734115516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
734215516c77SSepherosa Ziehau 			    "pktinfo overlap data, pktinfo abs %d len %d, "
734315516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
734415516c77SSepherosa Ziehau 			    pktinfo_off, pktinfo_len, data_off, data_len);
734515516c77SSepherosa Ziehau 			return;
734615516c77SSepherosa Ziehau 		}
734715516c77SSepherosa Ziehau 
734815516c77SSepherosa Ziehau 		/*
734915516c77SSepherosa Ziehau 		 * Find useful per-packet-info.
735015516c77SSepherosa Ziehau 		 */
735115516c77SSepherosa Ziehau 		error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
735215516c77SSepherosa Ziehau 		    pktinfo_len, &info);
735315516c77SSepherosa Ziehau 		if (__predict_false(error)) {
735415516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
735515516c77SSepherosa Ziehau 			    "pktinfo\n");
735615516c77SSepherosa Ziehau 			return;
735715516c77SSepherosa Ziehau 		}
735815516c77SSepherosa Ziehau 	}
735915516c77SSepherosa Ziehau 
736015516c77SSepherosa Ziehau 	if (__predict_false(data_off + data_len > pkt->rm_len)) {
736115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
736215516c77SSepherosa Ziehau 		    "data overflow, msglen %u, data abs %d len %d\n",
736315516c77SSepherosa Ziehau 		    pkt->rm_len, data_off, data_len);
736415516c77SSepherosa Ziehau 		return;
736515516c77SSepherosa Ziehau 	}
7366a491581fSWei Hu 
7367a491581fSWei Hu 	/* Identify RSC fragments, drop invalid packets */
7368a491581fSWei Hu 	if ((info.pktinfo_id != NULL) &&
7369a491581fSWei Hu 	    (info.pktinfo_id->flag & HN_NDIS_PKTINFO_SUBALLOC)) {
7370a491581fSWei Hu 		if (info.pktinfo_id->flag & HN_NDIS_PKTINFO_1ST_FRAG) {
7371a491581fSWei Hu 			rxr->rsc.cnt = 0;
7372a491581fSWei Hu 			rxr->hn_rsc_pkts++;
7373a491581fSWei Hu 		} else if (rxr->rsc.cnt == 0)
7374a491581fSWei Hu 			goto drop;
7375a491581fSWei Hu 
7376a491581fSWei Hu 		rsc_more = true;
7377a491581fSWei Hu 
7378a491581fSWei Hu 		if (info.pktinfo_id->flag & HN_NDIS_PKTINFO_LAST_FRAG)
7379a491581fSWei Hu 			rsc_more = false;
7380a491581fSWei Hu 
7381a491581fSWei Hu 		if (rsc_more && rxr->rsc.is_last)
7382a491581fSWei Hu 			goto drop;
7383a491581fSWei Hu 	} else {
7384a491581fSWei Hu 		rxr->rsc.cnt = 0;
7385a491581fSWei Hu 	}
7386a491581fSWei Hu 
7387a491581fSWei Hu 	if (__predict_false(rxr->rsc.cnt >= HN_NVS_RSC_MAX))
7388a491581fSWei Hu 		goto drop;
7389a491581fSWei Hu 
7390a491581fSWei Hu 	/* Store data in per rx ring structure */
7391a491581fSWei Hu 	hn_rsc_add_data(rxr,((const uint8_t *)pkt) + data_off,
7392a491581fSWei Hu 	    data_len, &info);
7393a491581fSWei Hu 
7394a491581fSWei Hu 	if (rsc_more)
7395a491581fSWei Hu 		return;
7396a491581fSWei Hu 
7397a491581fSWei Hu 	hn_rxpkt(rxr);
7398a491581fSWei Hu 	rxr->rsc.cnt = 0;
7399a491581fSWei Hu 	return;
7400a491581fSWei Hu drop:
7401a491581fSWei Hu 	rxr->hn_rsc_drop++;
7402a491581fSWei Hu 	return;
740315516c77SSepherosa Ziehau }
740415516c77SSepherosa Ziehau 
740515516c77SSepherosa Ziehau static __inline void
740615516c77SSepherosa Ziehau hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen)
740715516c77SSepherosa Ziehau {
740815516c77SSepherosa Ziehau 	const struct rndis_msghdr *hdr;
740915516c77SSepherosa Ziehau 
741015516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*hdr))) {
741115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
741215516c77SSepherosa Ziehau 		return;
741315516c77SSepherosa Ziehau 	}
741415516c77SSepherosa Ziehau 	hdr = data;
741515516c77SSepherosa Ziehau 
741615516c77SSepherosa Ziehau 	if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) {
741715516c77SSepherosa Ziehau 		/* Hot data path. */
741815516c77SSepherosa Ziehau 		hn_rndis_rx_data(rxr, data, dlen);
741915516c77SSepherosa Ziehau 		/* Done! */
742015516c77SSepherosa Ziehau 		return;
742115516c77SSepherosa Ziehau 	}
742215516c77SSepherosa Ziehau 
742315516c77SSepherosa Ziehau 	if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG)
742415516c77SSepherosa Ziehau 		hn_rndis_rx_status(rxr->hn_ifp->if_softc, data, dlen);
742515516c77SSepherosa Ziehau 	else
742615516c77SSepherosa Ziehau 		hn_rndis_rx_ctrl(rxr->hn_ifp->if_softc, data, dlen);
742715516c77SSepherosa Ziehau }
742815516c77SSepherosa Ziehau 
742915516c77SSepherosa Ziehau static void
743015516c77SSepherosa Ziehau hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
743115516c77SSepherosa Ziehau {
743215516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *hdr;
743315516c77SSepherosa Ziehau 
743415516c77SSepherosa Ziehau 	if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) {
743515516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid nvs notify\n");
743615516c77SSepherosa Ziehau 		return;
743715516c77SSepherosa Ziehau 	}
743815516c77SSepherosa Ziehau 	hdr = VMBUS_CHANPKT_CONST_DATA(pkt);
743915516c77SSepherosa Ziehau 
744015516c77SSepherosa Ziehau 	if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) {
744115516c77SSepherosa Ziehau 		/* Useless; ignore */
744215516c77SSepherosa Ziehau 		return;
744315516c77SSepherosa Ziehau 	}
744415516c77SSepherosa Ziehau 	if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type);
744515516c77SSepherosa Ziehau }
744615516c77SSepherosa Ziehau 
744715516c77SSepherosa Ziehau static void
744815516c77SSepherosa Ziehau hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
744915516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkt)
745015516c77SSepherosa Ziehau {
745115516c77SSepherosa Ziehau 	struct hn_nvs_sendctx *sndc;
745215516c77SSepherosa Ziehau 
745315516c77SSepherosa Ziehau 	sndc = (struct hn_nvs_sendctx *)(uintptr_t)pkt->cph_xactid;
745415516c77SSepherosa Ziehau 	sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt),
745515516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_DATALEN(pkt));
745615516c77SSepherosa Ziehau 	/*
745715516c77SSepherosa Ziehau 	 * NOTE:
745815516c77SSepherosa Ziehau 	 * 'sndc' CAN NOT be accessed anymore, since it can be freed by
745915516c77SSepherosa Ziehau 	 * its callback.
746015516c77SSepherosa Ziehau 	 */
746115516c77SSepherosa Ziehau }
746215516c77SSepherosa Ziehau 
746315516c77SSepherosa Ziehau static void
746415516c77SSepherosa Ziehau hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
746515516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkthdr)
746615516c77SSepherosa Ziehau {
746726d79d40SMichael Tuexen 	struct epoch_tracker et;
746815516c77SSepherosa Ziehau 	const struct vmbus_chanpkt_rxbuf *pkt;
746915516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *nvs_hdr;
747015516c77SSepherosa Ziehau 	int count, i, hlen;
747115516c77SSepherosa Ziehau 
747215516c77SSepherosa Ziehau 	if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) {
747315516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n");
747415516c77SSepherosa Ziehau 		return;
747515516c77SSepherosa Ziehau 	}
747615516c77SSepherosa Ziehau 	nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr);
747715516c77SSepherosa Ziehau 
747815516c77SSepherosa Ziehau 	/* Make sure that this is a RNDIS message. */
747915516c77SSepherosa Ziehau 	if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) {
748015516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n",
748115516c77SSepherosa Ziehau 		    nvs_hdr->nvs_type);
748215516c77SSepherosa Ziehau 		return;
748315516c77SSepherosa Ziehau 	}
748415516c77SSepherosa Ziehau 
748515516c77SSepherosa Ziehau 	hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen);
748615516c77SSepherosa Ziehau 	if (__predict_false(hlen < sizeof(*pkt))) {
748715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n");
748815516c77SSepherosa Ziehau 		return;
748915516c77SSepherosa Ziehau 	}
749015516c77SSepherosa Ziehau 	pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr;
749115516c77SSepherosa Ziehau 
749215516c77SSepherosa Ziehau 	if (__predict_false(pkt->cp_rxbuf_id != HN_NVS_RXBUF_SIG)) {
749315516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n",
749415516c77SSepherosa Ziehau 		    pkt->cp_rxbuf_id);
749515516c77SSepherosa Ziehau 		return;
749615516c77SSepherosa Ziehau 	}
749715516c77SSepherosa Ziehau 
749815516c77SSepherosa Ziehau 	count = pkt->cp_rxbuf_cnt;
749915516c77SSepherosa Ziehau 	if (__predict_false(hlen <
750015516c77SSepherosa Ziehau 	    __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) {
750115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count);
750215516c77SSepherosa Ziehau 		return;
750315516c77SSepherosa Ziehau 	}
750415516c77SSepherosa Ziehau 
750526d79d40SMichael Tuexen 	NET_EPOCH_ENTER(et);
750615516c77SSepherosa Ziehau 	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
750715516c77SSepherosa Ziehau 	for (i = 0; i < count; ++i) {
750815516c77SSepherosa Ziehau 		int ofs, len;
750915516c77SSepherosa Ziehau 
751015516c77SSepherosa Ziehau 		ofs = pkt->cp_rxbuf[i].rb_ofs;
751115516c77SSepherosa Ziehau 		len = pkt->cp_rxbuf[i].rb_len;
751215516c77SSepherosa Ziehau 		if (__predict_false(ofs + len > HN_RXBUF_SIZE)) {
751315516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, "
751415516c77SSepherosa Ziehau 			    "ofs %d, len %d\n", i, ofs, len);
751515516c77SSepherosa Ziehau 			continue;
751615516c77SSepherosa Ziehau 		}
7517a491581fSWei Hu 
7518a491581fSWei Hu 		rxr->rsc.is_last = (i == (count - 1));
751915516c77SSepherosa Ziehau 		hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len);
752015516c77SSepherosa Ziehau 	}
752126d79d40SMichael Tuexen 	NET_EPOCH_EXIT(et);
752215516c77SSepherosa Ziehau 
752315516c77SSepherosa Ziehau 	/*
752415516c77SSepherosa Ziehau 	 * Ack the consumed RXBUF associated w/ this channel packet,
752515516c77SSepherosa Ziehau 	 * so that this RXBUF can be recycled by the hypervisor.
752615516c77SSepherosa Ziehau 	 */
752715516c77SSepherosa Ziehau 	hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid);
752815516c77SSepherosa Ziehau }
752915516c77SSepherosa Ziehau 
753015516c77SSepherosa Ziehau static void
753115516c77SSepherosa Ziehau hn_nvs_ack_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
753215516c77SSepherosa Ziehau     uint64_t tid)
753315516c77SSepherosa Ziehau {
753415516c77SSepherosa Ziehau 	struct hn_nvs_rndis_ack ack;
753515516c77SSepherosa Ziehau 	int retries, error;
753615516c77SSepherosa Ziehau 
753715516c77SSepherosa Ziehau 	ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK;
753815516c77SSepherosa Ziehau 	ack.nvs_status = HN_NVS_STATUS_OK;
753915516c77SSepherosa Ziehau 
754015516c77SSepherosa Ziehau 	retries = 0;
754115516c77SSepherosa Ziehau again:
754215516c77SSepherosa Ziehau 	error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
754315516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid);
754415516c77SSepherosa Ziehau 	if (__predict_false(error == EAGAIN)) {
754515516c77SSepherosa Ziehau 		/*
754615516c77SSepherosa Ziehau 		 * NOTE:
754715516c77SSepherosa Ziehau 		 * This should _not_ happen in real world, since the
754815516c77SSepherosa Ziehau 		 * consumption of the TX bufring from the TX path is
754915516c77SSepherosa Ziehau 		 * controlled.
755015516c77SSepherosa Ziehau 		 */
755115516c77SSepherosa Ziehau 		if (rxr->hn_ack_failed == 0)
755215516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "RXBUF ack retry\n");
755315516c77SSepherosa Ziehau 		rxr->hn_ack_failed++;
755415516c77SSepherosa Ziehau 		retries++;
755515516c77SSepherosa Ziehau 		if (retries < 10) {
755615516c77SSepherosa Ziehau 			DELAY(100);
755715516c77SSepherosa Ziehau 			goto again;
755815516c77SSepherosa Ziehau 		}
755915516c77SSepherosa Ziehau 		/* RXBUF leaks! */
756015516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "RXBUF ack failed\n");
756115516c77SSepherosa Ziehau 	}
756215516c77SSepherosa Ziehau }
756315516c77SSepherosa Ziehau 
756415516c77SSepherosa Ziehau static void
756515516c77SSepherosa Ziehau hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
756615516c77SSepherosa Ziehau {
756715516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr = xrxr;
756815516c77SSepherosa Ziehau 	struct hn_softc *sc = rxr->hn_ifp->if_softc;
756915516c77SSepherosa Ziehau 
757015516c77SSepherosa Ziehau 	for (;;) {
757115516c77SSepherosa Ziehau 		struct vmbus_chanpkt_hdr *pkt = rxr->hn_pktbuf;
757215516c77SSepherosa Ziehau 		int error, pktlen;
757315516c77SSepherosa Ziehau 
757415516c77SSepherosa Ziehau 		pktlen = rxr->hn_pktbuf_len;
757515516c77SSepherosa Ziehau 		error = vmbus_chan_recv_pkt(chan, pkt, &pktlen);
757615516c77SSepherosa Ziehau 		if (__predict_false(error == ENOBUFS)) {
757715516c77SSepherosa Ziehau 			void *nbuf;
757815516c77SSepherosa Ziehau 			int nlen;
757915516c77SSepherosa Ziehau 
758015516c77SSepherosa Ziehau 			/*
758115516c77SSepherosa Ziehau 			 * Expand channel packet buffer.
758215516c77SSepherosa Ziehau 			 *
758315516c77SSepherosa Ziehau 			 * XXX
758415516c77SSepherosa Ziehau 			 * Use M_WAITOK here, since allocation failure
758515516c77SSepherosa Ziehau 			 * is fatal.
758615516c77SSepherosa Ziehau 			 */
758715516c77SSepherosa Ziehau 			nlen = rxr->hn_pktbuf_len * 2;
758815516c77SSepherosa Ziehau 			while (nlen < pktlen)
758915516c77SSepherosa Ziehau 				nlen *= 2;
759015516c77SSepherosa Ziehau 			nbuf = malloc(nlen, M_DEVBUF, M_WAITOK);
759115516c77SSepherosa Ziehau 
759215516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "expand pktbuf %d -> %d\n",
759315516c77SSepherosa Ziehau 			    rxr->hn_pktbuf_len, nlen);
759415516c77SSepherosa Ziehau 
759515516c77SSepherosa Ziehau 			free(rxr->hn_pktbuf, M_DEVBUF);
759615516c77SSepherosa Ziehau 			rxr->hn_pktbuf = nbuf;
759715516c77SSepherosa Ziehau 			rxr->hn_pktbuf_len = nlen;
759815516c77SSepherosa Ziehau 			/* Retry! */
759915516c77SSepherosa Ziehau 			continue;
760015516c77SSepherosa Ziehau 		} else if (__predict_false(error == EAGAIN)) {
760115516c77SSepherosa Ziehau 			/* No more channel packets; done! */
760215516c77SSepherosa Ziehau 			break;
760315516c77SSepherosa Ziehau 		}
760415516c77SSepherosa Ziehau 		KASSERT(!error, ("vmbus_chan_recv_pkt failed: %d", error));
760515516c77SSepherosa Ziehau 
760615516c77SSepherosa Ziehau 		switch (pkt->cph_type) {
760715516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_COMP:
760815516c77SSepherosa Ziehau 			hn_nvs_handle_comp(sc, chan, pkt);
760915516c77SSepherosa Ziehau 			break;
761015516c77SSepherosa Ziehau 
761115516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_RXBUF:
761215516c77SSepherosa Ziehau 			hn_nvs_handle_rxbuf(rxr, chan, pkt);
761315516c77SSepherosa Ziehau 			break;
761415516c77SSepherosa Ziehau 
761515516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_INBAND:
761615516c77SSepherosa Ziehau 			hn_nvs_handle_notify(sc, pkt);
761715516c77SSepherosa Ziehau 			break;
761815516c77SSepherosa Ziehau 
761915516c77SSepherosa Ziehau 		default:
762015516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "unknown chan pkt %u\n",
762115516c77SSepherosa Ziehau 			    pkt->cph_type);
762215516c77SSepherosa Ziehau 			break;
762315516c77SSepherosa Ziehau 		}
762415516c77SSepherosa Ziehau 	}
762515516c77SSepherosa Ziehau 	hn_chan_rollup(rxr, rxr->hn_txr);
762615516c77SSepherosa Ziehau }
762715516c77SSepherosa Ziehau 
762815516c77SSepherosa Ziehau static void
7629499c3e17SSepherosa Ziehau hn_sysinit(void *arg __unused)
763015516c77SSepherosa Ziehau {
7631fdd0222aSSepherosa Ziehau 	int i;
7632fdd0222aSSepherosa Ziehau 
76332be266caSSepherosa Ziehau 	hn_udpcs_fixup = counter_u64_alloc(M_WAITOK);
76342be266caSSepherosa Ziehau 
76359c6cae24SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
76369c6cae24SSepherosa Ziehau 	/*
76379c6cae24SSepherosa Ziehau 	 * Don't use ifnet.if_start if transparent VF mode is requested;
76389c6cae24SSepherosa Ziehau 	 * mainly due to the IFF_DRV_OACTIVE flag.
76399c6cae24SSepherosa Ziehau 	 */
76409c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_use_if_start) {
76419c6cae24SSepherosa Ziehau 		hn_use_if_start = 0;
76429c6cae24SSepherosa Ziehau 		printf("hn: tranparent VF mode, if_transmit will be used, "
76439c6cae24SSepherosa Ziehau 		    "instead of if_start\n");
76449c6cae24SSepherosa Ziehau 	}
76459c6cae24SSepherosa Ziehau #endif
76469c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_attwait < HN_XPNT_VF_ATTWAIT_MIN) {
76479c6cae24SSepherosa Ziehau 		printf("hn: invalid transparent VF attach routing "
76489c6cae24SSepherosa Ziehau 		    "wait timeout %d, reset to %d\n",
76499c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_attwait, HN_XPNT_VF_ATTWAIT_MIN);
76509c6cae24SSepherosa Ziehau 		hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
76519c6cae24SSepherosa Ziehau 	}
76529c6cae24SSepherosa Ziehau 
7653fdd0222aSSepherosa Ziehau 	/*
7654499c3e17SSepherosa Ziehau 	 * Initialize VF map.
7655499c3e17SSepherosa Ziehau 	 */
7656499c3e17SSepherosa Ziehau 	rm_init_flags(&hn_vfmap_lock, "hn_vfmap", RM_SLEEPABLE);
7657499c3e17SSepherosa Ziehau 	hn_vfmap_size = HN_VFMAP_SIZE_DEF;
7658499c3e17SSepherosa Ziehau 	hn_vfmap = malloc(sizeof(struct ifnet *) * hn_vfmap_size, M_DEVBUF,
7659499c3e17SSepherosa Ziehau 	    M_WAITOK | M_ZERO);
7660499c3e17SSepherosa Ziehau 
7661499c3e17SSepherosa Ziehau 	/*
7662fdd0222aSSepherosa Ziehau 	 * Fix the # of TX taskqueues.
7663fdd0222aSSepherosa Ziehau 	 */
7664fdd0222aSSepherosa Ziehau 	if (hn_tx_taskq_cnt <= 0)
7665fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = 1;
7666fdd0222aSSepherosa Ziehau 	else if (hn_tx_taskq_cnt > mp_ncpus)
7667fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = mp_ncpus;
766815516c77SSepherosa Ziehau 
76690e11868dSSepherosa Ziehau 	/*
76700e11868dSSepherosa Ziehau 	 * Fix the TX taskqueue mode.
76710e11868dSSepherosa Ziehau 	 */
76720e11868dSSepherosa Ziehau 	switch (hn_tx_taskq_mode) {
76730e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_INDEP:
76740e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_GLOBAL:
76750e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_EVTTQ:
76760e11868dSSepherosa Ziehau 		break;
76770e11868dSSepherosa Ziehau 	default:
76780e11868dSSepherosa Ziehau 		hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
76790e11868dSSepherosa Ziehau 		break;
76800e11868dSSepherosa Ziehau 	}
76810e11868dSSepherosa Ziehau 
768215516c77SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV)
768315516c77SSepherosa Ziehau 		return;
768415516c77SSepherosa Ziehau 
76850e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode != HN_TX_TASKQ_M_GLOBAL)
768615516c77SSepherosa Ziehau 		return;
768715516c77SSepherosa Ziehau 
7688fdd0222aSSepherosa Ziehau 	hn_tx_taskque = malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
7689fdd0222aSSepherosa Ziehau 	    M_DEVBUF, M_WAITOK);
7690fdd0222aSSepherosa Ziehau 	for (i = 0; i < hn_tx_taskq_cnt; ++i) {
7691fdd0222aSSepherosa Ziehau 		hn_tx_taskque[i] = taskqueue_create("hn_tx", M_WAITOK,
7692fdd0222aSSepherosa Ziehau 		    taskqueue_thread_enqueue, &hn_tx_taskque[i]);
7693fdd0222aSSepherosa Ziehau 		taskqueue_start_threads(&hn_tx_taskque[i], 1, PI_NET,
7694fdd0222aSSepherosa Ziehau 		    "hn tx%d", i);
7695fdd0222aSSepherosa Ziehau 	}
769615516c77SSepherosa Ziehau }
7697499c3e17SSepherosa Ziehau SYSINIT(hn_sysinit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysinit, NULL);
769815516c77SSepherosa Ziehau 
769915516c77SSepherosa Ziehau static void
7700499c3e17SSepherosa Ziehau hn_sysuninit(void *arg __unused)
770115516c77SSepherosa Ziehau {
770215516c77SSepherosa Ziehau 
7703fdd0222aSSepherosa Ziehau 	if (hn_tx_taskque != NULL) {
7704fdd0222aSSepherosa Ziehau 		int i;
7705fdd0222aSSepherosa Ziehau 
7706fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
7707fdd0222aSSepherosa Ziehau 			taskqueue_free(hn_tx_taskque[i]);
7708fdd0222aSSepherosa Ziehau 		free(hn_tx_taskque, M_DEVBUF);
7709fdd0222aSSepherosa Ziehau 	}
7710499c3e17SSepherosa Ziehau 
7711499c3e17SSepherosa Ziehau 	if (hn_vfmap != NULL)
7712499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
7713499c3e17SSepherosa Ziehau 	rm_destroy(&hn_vfmap_lock);
77142be266caSSepherosa Ziehau 
77152be266caSSepherosa Ziehau 	counter_u64_free(hn_udpcs_fixup);
771615516c77SSepherosa Ziehau }
7717499c3e17SSepherosa Ziehau SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL);
7718