xref: /freebsd/sys/dev/hyperv/netvsc/if_hn.c (revision 3bed4e54f8dcb536798699b189c8fb100a56545d)
115516c77SSepherosa Ziehau /*-
215516c77SSepherosa Ziehau  * Copyright (c) 2010-2012 Citrix Inc.
315516c77SSepherosa Ziehau  * Copyright (c) 2009-2012,2016 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>
6415516c77SSepherosa Ziehau #include <sys/bus.h>
6515516c77SSepherosa Ziehau #include <sys/kernel.h>
6615516c77SSepherosa Ziehau #include <sys/limits.h>
6715516c77SSepherosa Ziehau #include <sys/malloc.h>
6815516c77SSepherosa Ziehau #include <sys/mbuf.h>
6915516c77SSepherosa Ziehau #include <sys/module.h>
7015516c77SSepherosa Ziehau #include <sys/queue.h>
7115516c77SSepherosa Ziehau #include <sys/lock.h>
72499c3e17SSepherosa Ziehau #include <sys/rmlock.h>
73499c3e17SSepherosa Ziehau #include <sys/sbuf.h>
7415516c77SSepherosa Ziehau #include <sys/smp.h>
7515516c77SSepherosa Ziehau #include <sys/socket.h>
7615516c77SSepherosa Ziehau #include <sys/sockio.h>
7715516c77SSepherosa Ziehau #include <sys/sx.h>
7815516c77SSepherosa Ziehau #include <sys/sysctl.h>
7915516c77SSepherosa Ziehau #include <sys/systm.h>
8015516c77SSepherosa Ziehau #include <sys/taskqueue.h>
8115516c77SSepherosa Ziehau #include <sys/buf_ring.h>
825bdfd3fdSDexuan Cui #include <sys/eventhandler.h>
8315516c77SSepherosa Ziehau 
8415516c77SSepherosa Ziehau #include <machine/atomic.h>
8515516c77SSepherosa Ziehau #include <machine/in_cksum.h>
8615516c77SSepherosa Ziehau 
8715516c77SSepherosa Ziehau #include <net/bpf.h>
8815516c77SSepherosa Ziehau #include <net/ethernet.h>
8915516c77SSepherosa Ziehau #include <net/if.h>
905bdfd3fdSDexuan Cui #include <net/if_dl.h>
9115516c77SSepherosa Ziehau #include <net/if_media.h>
9215516c77SSepherosa Ziehau #include <net/if_types.h>
9315516c77SSepherosa Ziehau #include <net/if_var.h>
9415516c77SSepherosa Ziehau #include <net/rndis.h>
9534d68912SSepherosa Ziehau #ifdef RSS
9634d68912SSepherosa Ziehau #include <net/rss_config.h>
9734d68912SSepherosa Ziehau #endif
9815516c77SSepherosa Ziehau 
9915516c77SSepherosa Ziehau #include <netinet/in_systm.h>
10015516c77SSepherosa Ziehau #include <netinet/in.h>
10115516c77SSepherosa Ziehau #include <netinet/ip.h>
10215516c77SSepherosa Ziehau #include <netinet/ip6.h>
10315516c77SSepherosa Ziehau #include <netinet/tcp.h>
10415516c77SSepherosa Ziehau #include <netinet/tcp_lro.h>
10515516c77SSepherosa Ziehau #include <netinet/udp.h>
10615516c77SSepherosa Ziehau 
10715516c77SSepherosa Ziehau #include <dev/hyperv/include/hyperv.h>
10815516c77SSepherosa Ziehau #include <dev/hyperv/include/hyperv_busdma.h>
10915516c77SSepherosa Ziehau #include <dev/hyperv/include/vmbus.h>
11015516c77SSepherosa Ziehau #include <dev/hyperv/include/vmbus_xact.h>
11115516c77SSepherosa Ziehau 
11215516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/ndis.h>
11315516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/if_hnreg.h>
11415516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/if_hnvar.h>
11515516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/hn_nvs.h>
11615516c77SSepherosa Ziehau #include <dev/hyperv/netvsc/hn_rndis.h>
11715516c77SSepherosa Ziehau 
11815516c77SSepherosa Ziehau #include "vmbus_if.h"
11915516c77SSepherosa Ziehau 
12023bf9e15SSepherosa Ziehau #define HN_IFSTART_SUPPORT
12123bf9e15SSepherosa Ziehau 
12215516c77SSepherosa Ziehau #define HN_RING_CNT_DEF_MAX		8
12315516c77SSepherosa Ziehau 
124499c3e17SSepherosa Ziehau #define HN_VFMAP_SIZE_DEF		8
125499c3e17SSepherosa Ziehau 
1269c6cae24SSepherosa Ziehau #define HN_XPNT_VF_ATTWAIT_MIN		2	/* seconds */
1279c6cae24SSepherosa Ziehau 
12815516c77SSepherosa Ziehau /* YYY should get it from the underlying channel */
12915516c77SSepherosa Ziehau #define HN_TX_DESC_CNT			512
13015516c77SSepherosa Ziehau 
13115516c77SSepherosa Ziehau #define HN_RNDIS_PKT_LEN					\
13215516c77SSepherosa Ziehau 	(sizeof(struct rndis_packet_msg) +			\
13315516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(HN_NDIS_HASH_VALUE_SIZE) +	\
13415516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_VLAN_INFO_SIZE) +		\
13515516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_LSO2_INFO_SIZE) +		\
13615516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_TXCSUM_INFO_SIZE))
13715516c77SSepherosa Ziehau #define HN_RNDIS_PKT_BOUNDARY		PAGE_SIZE
13815516c77SSepherosa Ziehau #define HN_RNDIS_PKT_ALIGN		CACHE_LINE_SIZE
13915516c77SSepherosa Ziehau 
14015516c77SSepherosa Ziehau #define HN_TX_DATA_BOUNDARY		PAGE_SIZE
14115516c77SSepherosa Ziehau #define HN_TX_DATA_MAXSIZE		IP_MAXPACKET
14215516c77SSepherosa Ziehau #define HN_TX_DATA_SEGSIZE		PAGE_SIZE
14315516c77SSepherosa Ziehau /* -1 for RNDIS packet message */
14415516c77SSepherosa Ziehau #define HN_TX_DATA_SEGCNT_MAX		(HN_GPACNT_MAX - 1)
14515516c77SSepherosa Ziehau 
14615516c77SSepherosa Ziehau #define HN_DIRECT_TX_SIZE_DEF		128
14715516c77SSepherosa Ziehau 
14815516c77SSepherosa Ziehau #define HN_EARLY_TXEOF_THRESH		8
14915516c77SSepherosa Ziehau 
15015516c77SSepherosa Ziehau #define HN_PKTBUF_LEN_DEF		(16 * 1024)
15115516c77SSepherosa Ziehau 
15215516c77SSepherosa Ziehau #define HN_LROENT_CNT_DEF		128
15315516c77SSepherosa Ziehau 
15415516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MULTIRX_DEF	(12 * ETHERMTU)
15515516c77SSepherosa Ziehau #define HN_LRO_LENLIM_DEF		(25 * ETHERMTU)
15615516c77SSepherosa Ziehau /* YYY 2*MTU is a bit rough, but should be good enough. */
15715516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MIN(ifp)		(2 * (ifp)->if_mtu)
15815516c77SSepherosa Ziehau 
15915516c77SSepherosa Ziehau #define HN_LRO_ACKCNT_DEF		1
16015516c77SSepherosa Ziehau 
16115516c77SSepherosa Ziehau #define HN_LOCK_INIT(sc)		\
16215516c77SSepherosa Ziehau 	sx_init(&(sc)->hn_lock, device_get_nameunit((sc)->hn_dev))
16315516c77SSepherosa Ziehau #define HN_LOCK_DESTROY(sc)		sx_destroy(&(sc)->hn_lock)
16415516c77SSepherosa Ziehau #define HN_LOCK_ASSERT(sc)		sx_assert(&(sc)->hn_lock, SA_XLOCKED)
165fdc4f478SSepherosa Ziehau #define HN_LOCK(sc)					\
166fdc4f478SSepherosa Ziehau do {							\
167fdc4f478SSepherosa Ziehau 	while (sx_try_xlock(&(sc)->hn_lock) == 0)	\
168fdc4f478SSepherosa Ziehau 		DELAY(1000);				\
169fdc4f478SSepherosa Ziehau } while (0)
17015516c77SSepherosa Ziehau #define HN_UNLOCK(sc)			sx_xunlock(&(sc)->hn_lock)
17115516c77SSepherosa Ziehau 
17215516c77SSepherosa Ziehau #define HN_CSUM_IP_MASK			(CSUM_IP | CSUM_IP_TCP | CSUM_IP_UDP)
17315516c77SSepherosa Ziehau #define HN_CSUM_IP6_MASK		(CSUM_IP6_TCP | CSUM_IP6_UDP)
17415516c77SSepherosa Ziehau #define HN_CSUM_IP_HWASSIST(sc)		\
17515516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP_MASK)
17615516c77SSepherosa Ziehau #define HN_CSUM_IP6_HWASSIST(sc)	\
17715516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP6_MASK)
17815516c77SSepherosa Ziehau 
179dc13fee6SSepherosa Ziehau #define HN_PKTSIZE_MIN(align)		\
180dc13fee6SSepherosa Ziehau 	roundup2(ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN - ETHER_CRC_LEN + \
181dc13fee6SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN, (align))
182dc13fee6SSepherosa Ziehau #define HN_PKTSIZE(m, align)		\
183dc13fee6SSepherosa Ziehau 	roundup2((m)->m_pkthdr.len + HN_RNDIS_PKT_LEN, (align))
184dc13fee6SSepherosa Ziehau 
18534d68912SSepherosa Ziehau #ifdef RSS
18634d68912SSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	rss_getcpu((idx) % rss_getnumbuckets())
18734d68912SSepherosa Ziehau #else
1880e11868dSSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	(((sc)->hn_cpu + (idx)) % mp_ncpus)
18934d68912SSepherosa Ziehau #endif
1900e11868dSSepherosa Ziehau 
19115516c77SSepherosa Ziehau struct hn_txdesc {
19215516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
19315516c77SSepherosa Ziehau 	SLIST_ENTRY(hn_txdesc)		link;
19415516c77SSepherosa Ziehau #endif
195dc13fee6SSepherosa Ziehau 	STAILQ_ENTRY(hn_txdesc)		agg_link;
196dc13fee6SSepherosa Ziehau 
197dc13fee6SSepherosa Ziehau 	/* Aggregated txdescs, in sending order. */
198dc13fee6SSepherosa Ziehau 	STAILQ_HEAD(, hn_txdesc)	agg_list;
199dc13fee6SSepherosa Ziehau 
200dc13fee6SSepherosa Ziehau 	/* The oldest packet, if transmission aggregation happens. */
20115516c77SSepherosa Ziehau 	struct mbuf			*m;
20215516c77SSepherosa Ziehau 	struct hn_tx_ring		*txr;
20315516c77SSepherosa Ziehau 	int				refs;
20415516c77SSepherosa Ziehau 	uint32_t			flags;	/* HN_TXD_FLAG_ */
20515516c77SSepherosa Ziehau 	struct hn_nvs_sendctx		send_ctx;
20615516c77SSepherosa Ziehau 	uint32_t			chim_index;
20715516c77SSepherosa Ziehau 	int				chim_size;
20815516c77SSepherosa Ziehau 
20915516c77SSepherosa Ziehau 	bus_dmamap_t			data_dmap;
21015516c77SSepherosa Ziehau 
21115516c77SSepherosa Ziehau 	bus_addr_t			rndis_pkt_paddr;
21215516c77SSepherosa Ziehau 	struct rndis_packet_msg		*rndis_pkt;
21315516c77SSepherosa Ziehau 	bus_dmamap_t			rndis_pkt_dmap;
21415516c77SSepherosa Ziehau };
21515516c77SSepherosa Ziehau 
21615516c77SSepherosa Ziehau #define HN_TXD_FLAG_ONLIST		0x0001
21715516c77SSepherosa Ziehau #define HN_TXD_FLAG_DMAMAP		0x0002
218dc13fee6SSepherosa Ziehau #define HN_TXD_FLAG_ONAGG		0x0004
21915516c77SSepherosa Ziehau 
22015516c77SSepherosa Ziehau struct hn_rxinfo {
22115516c77SSepherosa Ziehau 	uint32_t			vlan_info;
22215516c77SSepherosa Ziehau 	uint32_t			csum_info;
22315516c77SSepherosa Ziehau 	uint32_t			hash_info;
22415516c77SSepherosa Ziehau 	uint32_t			hash_value;
22515516c77SSepherosa Ziehau };
22615516c77SSepherosa Ziehau 
227962f0357SSepherosa Ziehau struct hn_rxvf_setarg {
2285bdfd3fdSDexuan Cui 	struct hn_rx_ring	*rxr;
229962f0357SSepherosa Ziehau 	struct ifnet		*vf_ifp;
2305bdfd3fdSDexuan Cui };
2315bdfd3fdSDexuan Cui 
23215516c77SSepherosa Ziehau #define HN_RXINFO_VLAN			0x0001
23315516c77SSepherosa Ziehau #define HN_RXINFO_CSUM			0x0002
23415516c77SSepherosa Ziehau #define HN_RXINFO_HASHINF		0x0004
23515516c77SSepherosa Ziehau #define HN_RXINFO_HASHVAL		0x0008
23615516c77SSepherosa Ziehau #define HN_RXINFO_ALL			\
23715516c77SSepherosa Ziehau 	(HN_RXINFO_VLAN |		\
23815516c77SSepherosa Ziehau 	 HN_RXINFO_CSUM |		\
23915516c77SSepherosa Ziehau 	 HN_RXINFO_HASHINF |		\
24015516c77SSepherosa Ziehau 	 HN_RXINFO_HASHVAL)
24115516c77SSepherosa Ziehau 
24215516c77SSepherosa Ziehau #define HN_NDIS_VLAN_INFO_INVALID	0xffffffff
24315516c77SSepherosa Ziehau #define HN_NDIS_RXCSUM_INFO_INVALID	0
24415516c77SSepherosa Ziehau #define HN_NDIS_HASH_INFO_INVALID	0
24515516c77SSepherosa Ziehau 
24615516c77SSepherosa Ziehau static int			hn_probe(device_t);
24715516c77SSepherosa Ziehau static int			hn_attach(device_t);
24815516c77SSepherosa Ziehau static int			hn_detach(device_t);
24915516c77SSepherosa Ziehau static int			hn_shutdown(device_t);
25015516c77SSepherosa Ziehau static void			hn_chan_callback(struct vmbus_channel *,
25115516c77SSepherosa Ziehau 				    void *);
25215516c77SSepherosa Ziehau 
25315516c77SSepherosa Ziehau static void			hn_init(void *);
25415516c77SSepherosa Ziehau static int			hn_ioctl(struct ifnet *, u_long, caddr_t);
25523bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
25615516c77SSepherosa Ziehau static void			hn_start(struct ifnet *);
25723bf9e15SSepherosa Ziehau #endif
25815516c77SSepherosa Ziehau static int			hn_transmit(struct ifnet *, struct mbuf *);
25915516c77SSepherosa Ziehau static void			hn_xmit_qflush(struct ifnet *);
26015516c77SSepherosa Ziehau static int			hn_ifmedia_upd(struct ifnet *);
26115516c77SSepherosa Ziehau static void			hn_ifmedia_sts(struct ifnet *,
26215516c77SSepherosa Ziehau 				    struct ifmediareq *);
26315516c77SSepherosa Ziehau 
264499c3e17SSepherosa Ziehau static void			hn_ifnet_event(void *, struct ifnet *, int);
265499c3e17SSepherosa Ziehau static void			hn_ifaddr_event(void *, struct ifnet *);
266499c3e17SSepherosa Ziehau static void			hn_ifnet_attevent(void *, struct ifnet *);
267499c3e17SSepherosa Ziehau static void			hn_ifnet_detevent(void *, struct ifnet *);
2689c6cae24SSepherosa Ziehau static void			hn_ifnet_lnkevent(void *, struct ifnet *, int);
269499c3e17SSepherosa Ziehau 
270962f0357SSepherosa Ziehau static bool			hn_ismyvf(const struct hn_softc *,
271962f0357SSepherosa Ziehau 				    const struct ifnet *);
272962f0357SSepherosa Ziehau static void			hn_rxvf_change(struct hn_softc *,
273962f0357SSepherosa Ziehau 				    struct ifnet *, bool);
274962f0357SSepherosa Ziehau static void			hn_rxvf_set(struct hn_softc *, struct ifnet *);
275962f0357SSepherosa Ziehau static void			hn_rxvf_set_task(void *, int);
2769c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_input(struct ifnet *, struct mbuf *);
2779c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_iocsetflags(struct hn_softc *);
2789c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_iocsetcaps(struct hn_softc *,
2799c6cae24SSepherosa Ziehau 				    struct ifreq *);
2809c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_saveifflags(struct hn_softc *);
2819c6cae24SSepherosa Ziehau static bool			hn_xpnt_vf_isready(struct hn_softc *);
2829c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_setready(struct hn_softc *);
2839c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_init_taskfunc(void *, int);
2849c6cae24SSepherosa Ziehau static void			hn_xpnt_vf_init(struct hn_softc *);
285962f0357SSepherosa Ziehau 
28615516c77SSepherosa Ziehau static int			hn_rndis_rxinfo(const void *, int,
28715516c77SSepherosa Ziehau 				    struct hn_rxinfo *);
28815516c77SSepherosa Ziehau static void			hn_rndis_rx_data(struct hn_rx_ring *,
28915516c77SSepherosa Ziehau 				    const void *, int);
29015516c77SSepherosa Ziehau static void			hn_rndis_rx_status(struct hn_softc *,
29115516c77SSepherosa Ziehau 				    const void *, int);
292b3b75d9cSSepherosa Ziehau static void			hn_rndis_init_fixat(struct hn_softc *, int);
29315516c77SSepherosa Ziehau 
29415516c77SSepherosa Ziehau static void			hn_nvs_handle_notify(struct hn_softc *,
29515516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
29615516c77SSepherosa Ziehau static void			hn_nvs_handle_comp(struct hn_softc *,
29715516c77SSepherosa Ziehau 				    struct vmbus_channel *,
29815516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
29915516c77SSepherosa Ziehau static void			hn_nvs_handle_rxbuf(struct hn_rx_ring *,
30015516c77SSepherosa Ziehau 				    struct vmbus_channel *,
30115516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
30215516c77SSepherosa Ziehau static void			hn_nvs_ack_rxbuf(struct hn_rx_ring *,
30315516c77SSepherosa Ziehau 				    struct vmbus_channel *, uint64_t);
30415516c77SSepherosa Ziehau 
30515516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
30615516c77SSepherosa Ziehau static int			hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS);
30715516c77SSepherosa Ziehau static int			hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS);
30815516c77SSepherosa Ziehau #endif
30915516c77SSepherosa Ziehau static int			hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS);
31015516c77SSepherosa Ziehau static int			hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS);
31115516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
31215516c77SSepherosa Ziehau static int			hn_rx_stat_int_sysctl(SYSCTL_HANDLER_ARGS);
31315516c77SSepherosa Ziehau #else
31415516c77SSepherosa Ziehau static int			hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS);
31515516c77SSepherosa Ziehau #endif
31615516c77SSepherosa Ziehau static int			hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
31715516c77SSepherosa Ziehau static int			hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
31815516c77SSepherosa Ziehau static int			hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS);
31915516c77SSepherosa Ziehau static int			hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS);
32015516c77SSepherosa Ziehau static int			hn_caps_sysctl(SYSCTL_HANDLER_ARGS);
32115516c77SSepherosa Ziehau static int			hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS);
32215516c77SSepherosa Ziehau static int			hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS);
32334d68912SSepherosa Ziehau #ifndef RSS
32415516c77SSepherosa Ziehau static int			hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS);
32515516c77SSepherosa Ziehau static int			hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS);
32634d68912SSepherosa Ziehau #endif
32715516c77SSepherosa Ziehau static int			hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS);
328dc13fee6SSepherosa Ziehau static int			hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS);
329dc13fee6SSepherosa Ziehau static int			hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS);
330dc13fee6SSepherosa Ziehau static int			hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS);
331dc13fee6SSepherosa Ziehau static int			hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
3326c1204dfSSepherosa Ziehau static int			hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
33340d60d6eSDexuan Cui static int			hn_vf_sysctl(SYSCTL_HANDLER_ARGS);
334499c3e17SSepherosa Ziehau static int			hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS);
335499c3e17SSepherosa Ziehau static int			hn_vflist_sysctl(SYSCTL_HANDLER_ARGS);
336499c3e17SSepherosa Ziehau static int			hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS);
3379c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS);
3389c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS);
33915516c77SSepherosa Ziehau 
3405bdfd3fdSDexuan Cui static void			hn_stop(struct hn_softc *, bool);
34115516c77SSepherosa Ziehau static void			hn_init_locked(struct hn_softc *);
34215516c77SSepherosa Ziehau static int			hn_chan_attach(struct hn_softc *,
34315516c77SSepherosa Ziehau 				    struct vmbus_channel *);
34415516c77SSepherosa Ziehau static void			hn_chan_detach(struct hn_softc *,
34515516c77SSepherosa Ziehau 				    struct vmbus_channel *);
34615516c77SSepherosa Ziehau static int			hn_attach_subchans(struct hn_softc *);
34715516c77SSepherosa Ziehau static void			hn_detach_allchans(struct hn_softc *);
34815516c77SSepherosa Ziehau static void			hn_chan_rollup(struct hn_rx_ring *,
34915516c77SSepherosa Ziehau 				    struct hn_tx_ring *);
35015516c77SSepherosa Ziehau static void			hn_set_ring_inuse(struct hn_softc *, int);
35115516c77SSepherosa Ziehau static int			hn_synth_attach(struct hn_softc *, int);
35215516c77SSepherosa Ziehau static void			hn_synth_detach(struct hn_softc *);
35315516c77SSepherosa Ziehau static int			hn_synth_alloc_subchans(struct hn_softc *,
35415516c77SSepherosa Ziehau 				    int *);
3552494d735SSepherosa Ziehau static bool			hn_synth_attachable(const struct hn_softc *);
35615516c77SSepherosa Ziehau static void			hn_suspend(struct hn_softc *);
35715516c77SSepherosa Ziehau static void			hn_suspend_data(struct hn_softc *);
35815516c77SSepherosa Ziehau static void			hn_suspend_mgmt(struct hn_softc *);
35915516c77SSepherosa Ziehau static void			hn_resume(struct hn_softc *);
36015516c77SSepherosa Ziehau static void			hn_resume_data(struct hn_softc *);
36115516c77SSepherosa Ziehau static void			hn_resume_mgmt(struct hn_softc *);
36215516c77SSepherosa Ziehau static void			hn_suspend_mgmt_taskfunc(void *, int);
36325641fc7SSepherosa Ziehau static void			hn_chan_drain(struct hn_softc *,
36425641fc7SSepherosa Ziehau 				    struct vmbus_channel *);
365b3b75d9cSSepherosa Ziehau static void			hn_disable_rx(struct hn_softc *);
366b3b75d9cSSepherosa Ziehau static void			hn_drain_rxtx(struct hn_softc *, int);
3676c1204dfSSepherosa Ziehau static void			hn_polling(struct hn_softc *, u_int);
3686c1204dfSSepherosa Ziehau static void			hn_chan_polling(struct vmbus_channel *, u_int);
3699c6cae24SSepherosa Ziehau static void			hn_mtu_change_fixup(struct hn_softc *);
37015516c77SSepherosa Ziehau 
37115516c77SSepherosa Ziehau static void			hn_update_link_status(struct hn_softc *);
37215516c77SSepherosa Ziehau static void			hn_change_network(struct hn_softc *);
37315516c77SSepherosa Ziehau static void			hn_link_taskfunc(void *, int);
37415516c77SSepherosa Ziehau static void			hn_netchg_init_taskfunc(void *, int);
37515516c77SSepherosa Ziehau static void			hn_netchg_status_taskfunc(void *, int);
37615516c77SSepherosa Ziehau static void			hn_link_status(struct hn_softc *);
37715516c77SSepherosa Ziehau 
37815516c77SSepherosa Ziehau static int			hn_create_rx_data(struct hn_softc *, int);
37915516c77SSepherosa Ziehau static void			hn_destroy_rx_data(struct hn_softc *);
38015516c77SSepherosa Ziehau static int			hn_check_iplen(const struct mbuf *, int);
381f1b0a43fSSepherosa Ziehau static int			hn_set_rxfilter(struct hn_softc *, uint32_t);
382c08f7b2cSSepherosa Ziehau static int			hn_rxfilter_config(struct hn_softc *);
38334d68912SSepherosa Ziehau #ifndef RSS
38415516c77SSepherosa Ziehau static int			hn_rss_reconfig(struct hn_softc *);
38534d68912SSepherosa Ziehau #endif
386afd4971bSSepherosa Ziehau static void			hn_rss_ind_fixup(struct hn_softc *);
38715516c77SSepherosa Ziehau static int			hn_rxpkt(struct hn_rx_ring *, const void *,
38815516c77SSepherosa Ziehau 				    int, const struct hn_rxinfo *);
38915516c77SSepherosa Ziehau 
39015516c77SSepherosa Ziehau static int			hn_tx_ring_create(struct hn_softc *, int);
39115516c77SSepherosa Ziehau static void			hn_tx_ring_destroy(struct hn_tx_ring *);
39215516c77SSepherosa Ziehau static int			hn_create_tx_data(struct hn_softc *, int);
39315516c77SSepherosa Ziehau static void			hn_fixup_tx_data(struct hn_softc *);
39415516c77SSepherosa Ziehau static void			hn_destroy_tx_data(struct hn_softc *);
39515516c77SSepherosa Ziehau static void			hn_txdesc_dmamap_destroy(struct hn_txdesc *);
39625641fc7SSepherosa Ziehau static void			hn_txdesc_gc(struct hn_tx_ring *,
39725641fc7SSepherosa Ziehau 				    struct hn_txdesc *);
398dc13fee6SSepherosa Ziehau static int			hn_encap(struct ifnet *, struct hn_tx_ring *,
39915516c77SSepherosa Ziehau 				    struct hn_txdesc *, struct mbuf **);
40015516c77SSepherosa Ziehau static int			hn_txpkt(struct ifnet *, struct hn_tx_ring *,
40115516c77SSepherosa Ziehau 				    struct hn_txdesc *);
40215516c77SSepherosa Ziehau static void			hn_set_chim_size(struct hn_softc *, int);
40315516c77SSepherosa Ziehau static void			hn_set_tso_maxsize(struct hn_softc *, int, int);
40415516c77SSepherosa Ziehau static bool			hn_tx_ring_pending(struct hn_tx_ring *);
40515516c77SSepherosa Ziehau static void			hn_tx_ring_qflush(struct hn_tx_ring *);
40615516c77SSepherosa Ziehau static void			hn_resume_tx(struct hn_softc *, int);
407dc13fee6SSepherosa Ziehau static void			hn_set_txagg(struct hn_softc *);
408dc13fee6SSepherosa Ziehau static void			*hn_try_txagg(struct ifnet *,
409dc13fee6SSepherosa Ziehau 				    struct hn_tx_ring *, struct hn_txdesc *,
410dc13fee6SSepherosa Ziehau 				    int);
41115516c77SSepherosa Ziehau static int			hn_get_txswq_depth(const struct hn_tx_ring *);
41215516c77SSepherosa Ziehau static void			hn_txpkt_done(struct hn_nvs_sendctx *,
41315516c77SSepherosa Ziehau 				    struct hn_softc *, struct vmbus_channel *,
41415516c77SSepherosa Ziehau 				    const void *, int);
41515516c77SSepherosa Ziehau static int			hn_txpkt_sglist(struct hn_tx_ring *,
41615516c77SSepherosa Ziehau 				    struct hn_txdesc *);
41715516c77SSepherosa Ziehau static int			hn_txpkt_chim(struct hn_tx_ring *,
41815516c77SSepherosa Ziehau 				    struct hn_txdesc *);
41915516c77SSepherosa Ziehau static int			hn_xmit(struct hn_tx_ring *, int);
42015516c77SSepherosa Ziehau static void			hn_xmit_taskfunc(void *, int);
42115516c77SSepherosa Ziehau static void			hn_xmit_txeof(struct hn_tx_ring *);
42215516c77SSepherosa Ziehau static void			hn_xmit_txeof_taskfunc(void *, int);
42323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
42415516c77SSepherosa Ziehau static int			hn_start_locked(struct hn_tx_ring *, int);
42515516c77SSepherosa Ziehau static void			hn_start_taskfunc(void *, int);
42615516c77SSepherosa Ziehau static void			hn_start_txeof(struct hn_tx_ring *);
42715516c77SSepherosa Ziehau static void			hn_start_txeof_taskfunc(void *, int);
42823bf9e15SSepherosa Ziehau #endif
42915516c77SSepherosa Ziehau 
43015516c77SSepherosa Ziehau SYSCTL_NODE(_hw, OID_AUTO, hn, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
43115516c77SSepherosa Ziehau     "Hyper-V network interface");
43215516c77SSepherosa Ziehau 
43315516c77SSepherosa Ziehau /* Trust tcp segements verification on host side. */
43415516c77SSepherosa Ziehau static int			hn_trust_hosttcp = 1;
43515516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hosttcp, CTLFLAG_RDTUN,
43615516c77SSepherosa Ziehau     &hn_trust_hosttcp, 0,
43715516c77SSepherosa Ziehau     "Trust tcp segement verification on host side, "
43815516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
43915516c77SSepherosa Ziehau 
44015516c77SSepherosa Ziehau /* Trust udp datagrams verification on host side. */
44115516c77SSepherosa Ziehau static int			hn_trust_hostudp = 1;
44215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostudp, CTLFLAG_RDTUN,
44315516c77SSepherosa Ziehau     &hn_trust_hostudp, 0,
44415516c77SSepherosa Ziehau     "Trust udp datagram verification on host side, "
44515516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
44615516c77SSepherosa Ziehau 
44715516c77SSepherosa Ziehau /* Trust ip packets verification on host side. */
44815516c77SSepherosa Ziehau static int			hn_trust_hostip = 1;
44915516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostip, CTLFLAG_RDTUN,
45015516c77SSepherosa Ziehau     &hn_trust_hostip, 0,
45115516c77SSepherosa Ziehau     "Trust ip packet verification on host side, "
45215516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
45315516c77SSepherosa Ziehau 
45415516c77SSepherosa Ziehau /* Limit TSO burst size */
45515516c77SSepherosa Ziehau static int			hn_tso_maxlen = IP_MAXPACKET;
45615516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tso_maxlen, CTLFLAG_RDTUN,
45715516c77SSepherosa Ziehau     &hn_tso_maxlen, 0, "TSO burst limit");
45815516c77SSepherosa Ziehau 
45915516c77SSepherosa Ziehau /* Limit chimney send size */
46015516c77SSepherosa Ziehau static int			hn_tx_chimney_size = 0;
46115516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_chimney_size, CTLFLAG_RDTUN,
46215516c77SSepherosa Ziehau     &hn_tx_chimney_size, 0, "Chimney send packet size limit");
46315516c77SSepherosa Ziehau 
46415516c77SSepherosa Ziehau /* Limit the size of packet for direct transmission */
46515516c77SSepherosa Ziehau static int			hn_direct_tx_size = HN_DIRECT_TX_SIZE_DEF;
46615516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, direct_tx_size, CTLFLAG_RDTUN,
46715516c77SSepherosa Ziehau     &hn_direct_tx_size, 0, "Size of the packet for direct transmission");
46815516c77SSepherosa Ziehau 
46915516c77SSepherosa Ziehau /* # of LRO entries per RX ring */
47015516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
47115516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
47215516c77SSepherosa Ziehau static int			hn_lro_entry_count = HN_LROENT_CNT_DEF;
47315516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, lro_entry_count, CTLFLAG_RDTUN,
47415516c77SSepherosa Ziehau     &hn_lro_entry_count, 0, "LRO entry count");
47515516c77SSepherosa Ziehau #endif
47615516c77SSepherosa Ziehau #endif
47715516c77SSepherosa Ziehau 
478fdd0222aSSepherosa Ziehau static int			hn_tx_taskq_cnt = 1;
479fdd0222aSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_cnt, CTLFLAG_RDTUN,
480fdd0222aSSepherosa Ziehau     &hn_tx_taskq_cnt, 0, "# of TX taskqueues");
481fdd0222aSSepherosa Ziehau 
4820e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_INDEP	0
4830e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_GLOBAL	1
4840e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_EVTTQ	2
4850e11868dSSepherosa Ziehau 
4860e11868dSSepherosa Ziehau static int			hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
4870e11868dSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_mode, CTLFLAG_RDTUN,
4880e11868dSSepherosa Ziehau     &hn_tx_taskq_mode, 0, "TX taskqueue modes: "
4890e11868dSSepherosa Ziehau     "0 - independent, 1 - share global tx taskqs, 2 - share event taskqs");
4900e11868dSSepherosa Ziehau 
49115516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
49215516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 0;
49315516c77SSepherosa Ziehau #else
49415516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 1;
49515516c77SSepherosa Ziehau #endif
49615516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_txdesc_bufring, CTLFLAG_RD,
49715516c77SSepherosa Ziehau     &hn_use_txdesc_bufring, 0, "Use buf_ring for TX descriptors");
49815516c77SSepherosa Ziehau 
49923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
50015516c77SSepherosa Ziehau /* Use ifnet.if_start instead of ifnet.if_transmit */
50115516c77SSepherosa Ziehau static int			hn_use_if_start = 0;
50215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_if_start, CTLFLAG_RDTUN,
50315516c77SSepherosa Ziehau     &hn_use_if_start, 0, "Use if_start TX method");
50423bf9e15SSepherosa Ziehau #endif
50515516c77SSepherosa Ziehau 
50615516c77SSepherosa Ziehau /* # of channels to use */
50715516c77SSepherosa Ziehau static int			hn_chan_cnt = 0;
50815516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, chan_cnt, CTLFLAG_RDTUN,
50915516c77SSepherosa Ziehau     &hn_chan_cnt, 0,
51015516c77SSepherosa Ziehau     "# of channels to use; each channel has one RX ring and one TX ring");
51115516c77SSepherosa Ziehau 
51215516c77SSepherosa Ziehau /* # of transmit rings to use */
51315516c77SSepherosa Ziehau static int			hn_tx_ring_cnt = 0;
51415516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_ring_cnt, CTLFLAG_RDTUN,
51515516c77SSepherosa Ziehau     &hn_tx_ring_cnt, 0, "# of TX rings to use");
51615516c77SSepherosa Ziehau 
51715516c77SSepherosa Ziehau /* Software TX ring deptch */
51815516c77SSepherosa Ziehau static int			hn_tx_swq_depth = 0;
51915516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_swq_depth, CTLFLAG_RDTUN,
52015516c77SSepherosa Ziehau     &hn_tx_swq_depth, 0, "Depth of IFQ or BUFRING");
52115516c77SSepherosa Ziehau 
52215516c77SSepherosa Ziehau /* Enable sorted LRO, and the depth of the per-channel mbuf queue */
52315516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
52415516c77SSepherosa Ziehau static u_int			hn_lro_mbufq_depth = 0;
52515516c77SSepherosa Ziehau SYSCTL_UINT(_hw_hn, OID_AUTO, lro_mbufq_depth, CTLFLAG_RDTUN,
52615516c77SSepherosa Ziehau     &hn_lro_mbufq_depth, 0, "Depth of LRO mbuf queue");
52715516c77SSepherosa Ziehau #endif
52815516c77SSepherosa Ziehau 
529dc13fee6SSepherosa Ziehau /* Packet transmission aggregation size limit */
530dc13fee6SSepherosa Ziehau static int			hn_tx_agg_size = -1;
531dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_size, CTLFLAG_RDTUN,
532dc13fee6SSepherosa Ziehau     &hn_tx_agg_size, 0, "Packet transmission aggregation size limit");
533dc13fee6SSepherosa Ziehau 
534dc13fee6SSepherosa Ziehau /* Packet transmission aggregation count limit */
535fa915c4dSSepherosa Ziehau static int			hn_tx_agg_pkts = -1;
536dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_pkts, CTLFLAG_RDTUN,
537dc13fee6SSepherosa Ziehau     &hn_tx_agg_pkts, 0, "Packet transmission aggregation packet limit");
538dc13fee6SSepherosa Ziehau 
539499c3e17SSepherosa Ziehau /* VF list */
540499c3e17SSepherosa Ziehau SYSCTL_PROC(_hw_hn, OID_AUTO, vflist, CTLFLAG_RD | CTLTYPE_STRING,
541499c3e17SSepherosa Ziehau     0, 0, hn_vflist_sysctl, "A", "VF list");
542499c3e17SSepherosa Ziehau 
543499c3e17SSepherosa Ziehau /* VF mapping */
544499c3e17SSepherosa Ziehau SYSCTL_PROC(_hw_hn, OID_AUTO, vfmap, CTLFLAG_RD | CTLTYPE_STRING,
545499c3e17SSepherosa Ziehau     0, 0, hn_vfmap_sysctl, "A", "VF mapping");
546499c3e17SSepherosa Ziehau 
5479c6cae24SSepherosa Ziehau /* Transparent VF */
5489c6cae24SSepherosa Ziehau static int			hn_xpnt_vf = 0;
5499c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_transparent, CTLFLAG_RDTUN,
5509c6cae24SSepherosa Ziehau     &hn_xpnt_vf, 0, "Transparent VF mod");
5519c6cae24SSepherosa Ziehau 
5529c6cae24SSepherosa Ziehau /* Accurate BPF support for Transparent VF */
5539c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_accbpf = 0;
5549c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_accbpf, CTLFLAG_RDTUN,
5559c6cae24SSepherosa Ziehau     &hn_xpnt_vf_accbpf, 0, "Accurate BPF for transparent VF");
5569c6cae24SSepherosa Ziehau 
5579c6cae24SSepherosa Ziehau /* Extra wait for transparent VF attach routing; unit seconds. */
5589c6cae24SSepherosa Ziehau static int			hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
5599c6cae24SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, vf_xpnt_attwait, CTLFLAG_RWTUN,
5609c6cae24SSepherosa Ziehau     &hn_xpnt_vf_attwait, 0,
5619c6cae24SSepherosa Ziehau     "Extra wait for transparent VF attach routing; unit: seconds");
5629c6cae24SSepherosa Ziehau 
56315516c77SSepherosa Ziehau static u_int			hn_cpu_index;	/* next CPU for channel */
564fdd0222aSSepherosa Ziehau static struct taskqueue		**hn_tx_taskque;/* shared TX taskqueues */
56515516c77SSepherosa Ziehau 
566499c3e17SSepherosa Ziehau static struct rmlock		hn_vfmap_lock;
567499c3e17SSepherosa Ziehau static int			hn_vfmap_size;
568499c3e17SSepherosa Ziehau static struct ifnet		**hn_vfmap;
569499c3e17SSepherosa Ziehau 
57034d68912SSepherosa Ziehau #ifndef RSS
57115516c77SSepherosa Ziehau static const uint8_t
57215516c77SSepherosa Ziehau hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
57315516c77SSepherosa Ziehau 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
57415516c77SSepherosa Ziehau 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
57515516c77SSepherosa Ziehau 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
57615516c77SSepherosa Ziehau 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
57715516c77SSepherosa Ziehau 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
57815516c77SSepherosa Ziehau };
57934d68912SSepherosa Ziehau #endif	/* !RSS */
58015516c77SSepherosa Ziehau 
58115516c77SSepherosa Ziehau static device_method_t hn_methods[] = {
58215516c77SSepherosa Ziehau 	/* Device interface */
58315516c77SSepherosa Ziehau 	DEVMETHOD(device_probe,		hn_probe),
58415516c77SSepherosa Ziehau 	DEVMETHOD(device_attach,	hn_attach),
58515516c77SSepherosa Ziehau 	DEVMETHOD(device_detach,	hn_detach),
58615516c77SSepherosa Ziehau 	DEVMETHOD(device_shutdown,	hn_shutdown),
58715516c77SSepherosa Ziehau 	DEVMETHOD_END
58815516c77SSepherosa Ziehau };
58915516c77SSepherosa Ziehau 
59015516c77SSepherosa Ziehau static driver_t hn_driver = {
59115516c77SSepherosa Ziehau 	"hn",
59215516c77SSepherosa Ziehau 	hn_methods,
59315516c77SSepherosa Ziehau 	sizeof(struct hn_softc)
59415516c77SSepherosa Ziehau };
59515516c77SSepherosa Ziehau 
59615516c77SSepherosa Ziehau static devclass_t hn_devclass;
59715516c77SSepherosa Ziehau 
59815516c77SSepherosa Ziehau DRIVER_MODULE(hn, vmbus, hn_driver, hn_devclass, 0, 0);
59915516c77SSepherosa Ziehau MODULE_VERSION(hn, 1);
60015516c77SSepherosa Ziehau MODULE_DEPEND(hn, vmbus, 1, 1, 1);
60115516c77SSepherosa Ziehau 
60215516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
60315516c77SSepherosa Ziehau static void
60415516c77SSepherosa Ziehau hn_set_lro_lenlim(struct hn_softc *sc, int lenlim)
60515516c77SSepherosa Ziehau {
60615516c77SSepherosa Ziehau 	int i;
60715516c77SSepherosa Ziehau 
608a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
60915516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_length_lim = lenlim;
61015516c77SSepherosa Ziehau }
61115516c77SSepherosa Ziehau #endif
61215516c77SSepherosa Ziehau 
61315516c77SSepherosa Ziehau static int
61415516c77SSepherosa Ziehau hn_txpkt_sglist(struct hn_tx_ring *txr, struct hn_txdesc *txd)
61515516c77SSepherosa Ziehau {
61615516c77SSepherosa Ziehau 
61715516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
61815516c77SSepherosa Ziehau 	    txd->chim_size == 0, ("invalid rndis sglist txd"));
61915516c77SSepherosa Ziehau 	return (hn_nvs_send_rndis_sglist(txr->hn_chan, HN_NVS_RNDIS_MTYPE_DATA,
62015516c77SSepherosa Ziehau 	    &txd->send_ctx, txr->hn_gpa, txr->hn_gpa_cnt));
62115516c77SSepherosa Ziehau }
62215516c77SSepherosa Ziehau 
62315516c77SSepherosa Ziehau static int
62415516c77SSepherosa Ziehau hn_txpkt_chim(struct hn_tx_ring *txr, struct hn_txdesc *txd)
62515516c77SSepherosa Ziehau {
62615516c77SSepherosa Ziehau 	struct hn_nvs_rndis rndis;
62715516c77SSepherosa Ziehau 
62815516c77SSepherosa Ziehau 	KASSERT(txd->chim_index != HN_NVS_CHIM_IDX_INVALID &&
62915516c77SSepherosa Ziehau 	    txd->chim_size > 0, ("invalid rndis chim txd"));
63015516c77SSepherosa Ziehau 
63115516c77SSepherosa Ziehau 	rndis.nvs_type = HN_NVS_TYPE_RNDIS;
63215516c77SSepherosa Ziehau 	rndis.nvs_rndis_mtype = HN_NVS_RNDIS_MTYPE_DATA;
63315516c77SSepherosa Ziehau 	rndis.nvs_chim_idx = txd->chim_index;
63415516c77SSepherosa Ziehau 	rndis.nvs_chim_sz = txd->chim_size;
63515516c77SSepherosa Ziehau 
63615516c77SSepherosa Ziehau 	return (hn_nvs_send(txr->hn_chan, VMBUS_CHANPKT_FLAG_RC,
63715516c77SSepherosa Ziehau 	    &rndis, sizeof(rndis), &txd->send_ctx));
63815516c77SSepherosa Ziehau }
63915516c77SSepherosa Ziehau 
64015516c77SSepherosa Ziehau static __inline uint32_t
64115516c77SSepherosa Ziehau hn_chim_alloc(struct hn_softc *sc)
64215516c77SSepherosa Ziehau {
64315516c77SSepherosa Ziehau 	int i, bmap_cnt = sc->hn_chim_bmap_cnt;
64415516c77SSepherosa Ziehau 	u_long *bmap = sc->hn_chim_bmap;
64515516c77SSepherosa Ziehau 	uint32_t ret = HN_NVS_CHIM_IDX_INVALID;
64615516c77SSepherosa Ziehau 
64715516c77SSepherosa Ziehau 	for (i = 0; i < bmap_cnt; ++i) {
64815516c77SSepherosa Ziehau 		int idx;
64915516c77SSepherosa Ziehau 
65015516c77SSepherosa Ziehau 		idx = ffsl(~bmap[i]);
65115516c77SSepherosa Ziehau 		if (idx == 0)
65215516c77SSepherosa Ziehau 			continue;
65315516c77SSepherosa Ziehau 
65415516c77SSepherosa Ziehau 		--idx; /* ffsl is 1-based */
65515516c77SSepherosa Ziehau 		KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt,
65615516c77SSepherosa Ziehau 		    ("invalid i %d and idx %d", i, idx));
65715516c77SSepherosa Ziehau 
65815516c77SSepherosa Ziehau 		if (atomic_testandset_long(&bmap[i], idx))
65915516c77SSepherosa Ziehau 			continue;
66015516c77SSepherosa Ziehau 
66115516c77SSepherosa Ziehau 		ret = i * LONG_BIT + idx;
66215516c77SSepherosa Ziehau 		break;
66315516c77SSepherosa Ziehau 	}
66415516c77SSepherosa Ziehau 	return (ret);
66515516c77SSepherosa Ziehau }
66615516c77SSepherosa Ziehau 
66715516c77SSepherosa Ziehau static __inline void
66815516c77SSepherosa Ziehau hn_chim_free(struct hn_softc *sc, uint32_t chim_idx)
66915516c77SSepherosa Ziehau {
67015516c77SSepherosa Ziehau 	u_long mask;
67115516c77SSepherosa Ziehau 	uint32_t idx;
67215516c77SSepherosa Ziehau 
67315516c77SSepherosa Ziehau 	idx = chim_idx / LONG_BIT;
67415516c77SSepherosa Ziehau 	KASSERT(idx < sc->hn_chim_bmap_cnt,
67515516c77SSepherosa Ziehau 	    ("invalid chimney index 0x%x", chim_idx));
67615516c77SSepherosa Ziehau 
67715516c77SSepherosa Ziehau 	mask = 1UL << (chim_idx % LONG_BIT);
67815516c77SSepherosa Ziehau 	KASSERT(sc->hn_chim_bmap[idx] & mask,
67915516c77SSepherosa Ziehau 	    ("index bitmap 0x%lx, chimney index %u, "
68015516c77SSepherosa Ziehau 	     "bitmap idx %d, bitmask 0x%lx",
68115516c77SSepherosa Ziehau 	     sc->hn_chim_bmap[idx], chim_idx, idx, mask));
68215516c77SSepherosa Ziehau 
68315516c77SSepherosa Ziehau 	atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
68415516c77SSepherosa Ziehau }
68515516c77SSepherosa Ziehau 
686edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
687cc0c6ebcSSepherosa Ziehau 
688cc0c6ebcSSepherosa Ziehau #define PULLUP_HDR(m, len)				\
689cc0c6ebcSSepherosa Ziehau do {							\
690cc0c6ebcSSepherosa Ziehau 	if (__predict_false((m)->m_len < (len))) {	\
691cc0c6ebcSSepherosa Ziehau 		(m) = m_pullup((m), (len));		\
692cc0c6ebcSSepherosa Ziehau 		if ((m) == NULL)			\
693cc0c6ebcSSepherosa Ziehau 			return (NULL);			\
694cc0c6ebcSSepherosa Ziehau 	}						\
695cc0c6ebcSSepherosa Ziehau } while (0)
696cc0c6ebcSSepherosa Ziehau 
697edd3f315SSepherosa Ziehau /*
698edd3f315SSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
699edd3f315SSepherosa Ziehau  */
700edd3f315SSepherosa Ziehau static __inline struct mbuf *
701edd3f315SSepherosa Ziehau hn_tso_fixup(struct mbuf *m_head)
702edd3f315SSepherosa Ziehau {
703edd3f315SSepherosa Ziehau 	struct ether_vlan_header *evl;
704edd3f315SSepherosa Ziehau 	struct tcphdr *th;
705edd3f315SSepherosa Ziehau 	int ehlen;
706edd3f315SSepherosa Ziehau 
707edd3f315SSepherosa Ziehau 	KASSERT(M_WRITABLE(m_head), ("TSO mbuf not writable"));
708edd3f315SSepherosa Ziehau 
709edd3f315SSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
710edd3f315SSepherosa Ziehau 	evl = mtod(m_head, struct ether_vlan_header *);
711edd3f315SSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
712edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
713edd3f315SSepherosa Ziehau 	else
714edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
715edd3f315SSepherosa Ziehau 
716edd3f315SSepherosa Ziehau #ifdef INET
717edd3f315SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
718edd3f315SSepherosa Ziehau 		struct ip *ip;
719edd3f315SSepherosa Ziehau 		int iphlen;
720edd3f315SSepherosa Ziehau 
721edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
722edd3f315SSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
723edd3f315SSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
724edd3f315SSepherosa Ziehau 
725edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
726edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + iphlen);
727edd3f315SSepherosa Ziehau 
728edd3f315SSepherosa Ziehau 		ip->ip_len = 0;
729edd3f315SSepherosa Ziehau 		ip->ip_sum = 0;
730edd3f315SSepherosa Ziehau 		th->th_sum = in_pseudo(ip->ip_src.s_addr,
731edd3f315SSepherosa Ziehau 		    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
732edd3f315SSepherosa Ziehau 	}
733edd3f315SSepherosa Ziehau #endif
734edd3f315SSepherosa Ziehau #if defined(INET6) && defined(INET)
735edd3f315SSepherosa Ziehau 	else
736edd3f315SSepherosa Ziehau #endif
737edd3f315SSepherosa Ziehau #ifdef INET6
738edd3f315SSepherosa Ziehau 	{
739edd3f315SSepherosa Ziehau 		struct ip6_hdr *ip6;
740edd3f315SSepherosa Ziehau 
741edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
742edd3f315SSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
743edd3f315SSepherosa Ziehau 		if (ip6->ip6_nxt != IPPROTO_TCP) {
744edd3f315SSepherosa Ziehau 			m_freem(m_head);
745edd3f315SSepherosa Ziehau 			return (NULL);
746edd3f315SSepherosa Ziehau 		}
747edd3f315SSepherosa Ziehau 
748edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
749edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + sizeof(*ip6));
750edd3f315SSepherosa Ziehau 
751edd3f315SSepherosa Ziehau 		ip6->ip6_plen = 0;
752edd3f315SSepherosa Ziehau 		th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
753edd3f315SSepherosa Ziehau 	}
754edd3f315SSepherosa Ziehau #endif
755edd3f315SSepherosa Ziehau 	return (m_head);
756edd3f315SSepherosa Ziehau 
757edd3f315SSepherosa Ziehau }
758cc0c6ebcSSepherosa Ziehau 
759cc0c6ebcSSepherosa Ziehau /*
760cc0c6ebcSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
761cc0c6ebcSSepherosa Ziehau  */
762cc0c6ebcSSepherosa Ziehau static __inline struct mbuf *
763cc0c6ebcSSepherosa Ziehau hn_check_tcpsyn(struct mbuf *m_head, int *tcpsyn)
764cc0c6ebcSSepherosa Ziehau {
765cc0c6ebcSSepherosa Ziehau 	const struct ether_vlan_header *evl;
766cc0c6ebcSSepherosa Ziehau 	const struct tcphdr *th;
767cc0c6ebcSSepherosa Ziehau 	int ehlen;
768cc0c6ebcSSepherosa Ziehau 
769cc0c6ebcSSepherosa Ziehau 	*tcpsyn = 0;
770cc0c6ebcSSepherosa Ziehau 
771cc0c6ebcSSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
772cc0c6ebcSSepherosa Ziehau 	evl = mtod(m_head, const struct ether_vlan_header *);
773cc0c6ebcSSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
774cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
775cc0c6ebcSSepherosa Ziehau 	else
776cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
777cc0c6ebcSSepherosa Ziehau 
778cc0c6ebcSSepherosa Ziehau #ifdef INET
779cc0c6ebcSSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_IP_TCP) {
780cc0c6ebcSSepherosa Ziehau 		const struct ip *ip;
781cc0c6ebcSSepherosa Ziehau 		int iphlen;
782cc0c6ebcSSepherosa Ziehau 
783cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
784cc0c6ebcSSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
785cc0c6ebcSSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
786cc0c6ebcSSepherosa Ziehau 
787cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
788cc0c6ebcSSepherosa Ziehau 		th = mtodo(m_head, ehlen + iphlen);
789cc0c6ebcSSepherosa Ziehau 		if (th->th_flags & TH_SYN)
790cc0c6ebcSSepherosa Ziehau 			*tcpsyn = 1;
791cc0c6ebcSSepherosa Ziehau 	}
792cc0c6ebcSSepherosa Ziehau #endif
793cc0c6ebcSSepherosa Ziehau #if defined(INET6) && defined(INET)
794cc0c6ebcSSepherosa Ziehau 	else
795cc0c6ebcSSepherosa Ziehau #endif
796cc0c6ebcSSepherosa Ziehau #ifdef INET6
797cc0c6ebcSSepherosa Ziehau 	{
798cc0c6ebcSSepherosa Ziehau 		const struct ip6_hdr *ip6;
799cc0c6ebcSSepherosa Ziehau 
800cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
801cc0c6ebcSSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
802cc0c6ebcSSepherosa Ziehau 		if (ip6->ip6_nxt != IPPROTO_TCP)
803cc0c6ebcSSepherosa Ziehau 			return (m_head);
804cc0c6ebcSSepherosa Ziehau 
805cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
806cc0c6ebcSSepherosa Ziehau 		th = mtodo(m_head, ehlen + sizeof(*ip6));
807cc0c6ebcSSepherosa Ziehau 		if (th->th_flags & TH_SYN)
808cc0c6ebcSSepherosa Ziehau 			*tcpsyn = 1;
809cc0c6ebcSSepherosa Ziehau 	}
810cc0c6ebcSSepherosa Ziehau #endif
811cc0c6ebcSSepherosa Ziehau 	return (m_head);
812cc0c6ebcSSepherosa Ziehau }
813cc0c6ebcSSepherosa Ziehau 
814cc0c6ebcSSepherosa Ziehau #undef PULLUP_HDR
815cc0c6ebcSSepherosa Ziehau 
816edd3f315SSepherosa Ziehau #endif	/* INET6 || INET */
817edd3f315SSepherosa Ziehau 
81815516c77SSepherosa Ziehau static int
819f1b0a43fSSepherosa Ziehau hn_set_rxfilter(struct hn_softc *sc, uint32_t filter)
820f1b0a43fSSepherosa Ziehau {
821f1b0a43fSSepherosa Ziehau 	int error = 0;
822f1b0a43fSSepherosa Ziehau 
823f1b0a43fSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
824f1b0a43fSSepherosa Ziehau 
825f1b0a43fSSepherosa Ziehau 	if (sc->hn_rx_filter != filter) {
826f1b0a43fSSepherosa Ziehau 		error = hn_rndis_set_rxfilter(sc, filter);
827f1b0a43fSSepherosa Ziehau 		if (!error)
828f1b0a43fSSepherosa Ziehau 			sc->hn_rx_filter = filter;
829f1b0a43fSSepherosa Ziehau 	}
830f1b0a43fSSepherosa Ziehau 	return (error);
831f1b0a43fSSepherosa Ziehau }
832f1b0a43fSSepherosa Ziehau 
833f1b0a43fSSepherosa Ziehau static int
834c08f7b2cSSepherosa Ziehau hn_rxfilter_config(struct hn_softc *sc)
83515516c77SSepherosa Ziehau {
83615516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
83715516c77SSepherosa Ziehau 	uint32_t filter;
83815516c77SSepherosa Ziehau 
83915516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
84015516c77SSepherosa Ziehau 
8419c6cae24SSepherosa Ziehau 	/*
8429c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, we don't know how
8439c6cae24SSepherosa Ziehau 	 * its RX filter is configured, so stick the synthetic device in
8449c6cae24SSepherosa Ziehau 	 * the promiscous mode.
8459c6cae24SSepherosa Ziehau 	 */
8469c6cae24SSepherosa Ziehau 	if ((ifp->if_flags & IFF_PROMISC) || (sc->hn_flags & HN_FLAG_RXVF)) {
84715516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
84815516c77SSepherosa Ziehau 	} else {
84915516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_DIRECTED;
85015516c77SSepherosa Ziehau 		if (ifp->if_flags & IFF_BROADCAST)
85115516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_BROADCAST;
85215516c77SSepherosa Ziehau 		/* TODO: support multicast list */
85315516c77SSepherosa Ziehau 		if ((ifp->if_flags & IFF_ALLMULTI) ||
85415516c77SSepherosa Ziehau 		    !TAILQ_EMPTY(&ifp->if_multiaddrs))
85515516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
85615516c77SSepherosa Ziehau 	}
857f1b0a43fSSepherosa Ziehau 	return (hn_set_rxfilter(sc, filter));
85815516c77SSepherosa Ziehau }
85915516c77SSepherosa Ziehau 
860dc13fee6SSepherosa Ziehau static void
861dc13fee6SSepherosa Ziehau hn_set_txagg(struct hn_softc *sc)
862dc13fee6SSepherosa Ziehau {
863dc13fee6SSepherosa Ziehau 	uint32_t size, pkts;
864dc13fee6SSepherosa Ziehau 	int i;
865dc13fee6SSepherosa Ziehau 
866dc13fee6SSepherosa Ziehau 	/*
867dc13fee6SSepherosa Ziehau 	 * Setup aggregation size.
868dc13fee6SSepherosa Ziehau 	 */
869dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_size < 0)
870dc13fee6SSepherosa Ziehau 		size = UINT32_MAX;
871dc13fee6SSepherosa Ziehau 	else
872dc13fee6SSepherosa Ziehau 		size = sc->hn_agg_size;
873dc13fee6SSepherosa Ziehau 
874dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_size < size)
875dc13fee6SSepherosa Ziehau 		size = sc->hn_rndis_agg_size;
876dc13fee6SSepherosa Ziehau 
877a4364cfeSSepherosa Ziehau 	/* NOTE: We only aggregate packets using chimney sending buffers. */
878a4364cfeSSepherosa Ziehau 	if (size > (uint32_t)sc->hn_chim_szmax)
879a4364cfeSSepherosa Ziehau 		size = sc->hn_chim_szmax;
880a4364cfeSSepherosa Ziehau 
881dc13fee6SSepherosa Ziehau 	if (size <= 2 * HN_PKTSIZE_MIN(sc->hn_rndis_agg_align)) {
882dc13fee6SSepherosa Ziehau 		/* Disable */
883dc13fee6SSepherosa Ziehau 		size = 0;
884dc13fee6SSepherosa Ziehau 		pkts = 0;
885dc13fee6SSepherosa Ziehau 		goto done;
886dc13fee6SSepherosa Ziehau 	}
887dc13fee6SSepherosa Ziehau 
888dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'int'. */
889dc13fee6SSepherosa Ziehau 	if (size > INT_MAX)
890dc13fee6SSepherosa Ziehau 		size = INT_MAX;
891dc13fee6SSepherosa Ziehau 
892dc13fee6SSepherosa Ziehau 	/*
893dc13fee6SSepherosa Ziehau 	 * Setup aggregation packet count.
894dc13fee6SSepherosa Ziehau 	 */
895dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_pkts < 0)
896dc13fee6SSepherosa Ziehau 		pkts = UINT32_MAX;
897dc13fee6SSepherosa Ziehau 	else
898dc13fee6SSepherosa Ziehau 		pkts = sc->hn_agg_pkts;
899dc13fee6SSepherosa Ziehau 
900dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_pkts < pkts)
901dc13fee6SSepherosa Ziehau 		pkts = sc->hn_rndis_agg_pkts;
902dc13fee6SSepherosa Ziehau 
903dc13fee6SSepherosa Ziehau 	if (pkts <= 1) {
904dc13fee6SSepherosa Ziehau 		/* Disable */
905dc13fee6SSepherosa Ziehau 		size = 0;
906dc13fee6SSepherosa Ziehau 		pkts = 0;
907dc13fee6SSepherosa Ziehau 		goto done;
908dc13fee6SSepherosa Ziehau 	}
909dc13fee6SSepherosa Ziehau 
910dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
911dc13fee6SSepherosa Ziehau 	if (pkts > SHRT_MAX)
912dc13fee6SSepherosa Ziehau 		pkts = SHRT_MAX;
913dc13fee6SSepherosa Ziehau 
914dc13fee6SSepherosa Ziehau done:
915dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
916dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_align > SHRT_MAX) {
917dc13fee6SSepherosa Ziehau 		/* Disable */
918dc13fee6SSepherosa Ziehau 		size = 0;
919dc13fee6SSepherosa Ziehau 		pkts = 0;
920dc13fee6SSepherosa Ziehau 	}
921dc13fee6SSepherosa Ziehau 
922dc13fee6SSepherosa Ziehau 	if (bootverbose) {
923dc13fee6SSepherosa Ziehau 		if_printf(sc->hn_ifp, "TX agg size %u, pkts %u, align %u\n",
924dc13fee6SSepherosa Ziehau 		    size, pkts, sc->hn_rndis_agg_align);
925dc13fee6SSepherosa Ziehau 	}
926dc13fee6SSepherosa Ziehau 
927dc13fee6SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
928dc13fee6SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
929dc13fee6SSepherosa Ziehau 
930dc13fee6SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
931dc13fee6SSepherosa Ziehau 		txr->hn_agg_szmax = size;
932dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktmax = pkts;
933dc13fee6SSepherosa Ziehau 		txr->hn_agg_align = sc->hn_rndis_agg_align;
934dc13fee6SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
935dc13fee6SSepherosa Ziehau 	}
936dc13fee6SSepherosa Ziehau }
937dc13fee6SSepherosa Ziehau 
93815516c77SSepherosa Ziehau static int
93915516c77SSepherosa Ziehau hn_get_txswq_depth(const struct hn_tx_ring *txr)
94015516c77SSepherosa Ziehau {
94115516c77SSepherosa Ziehau 
94215516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_cnt > 0, ("tx ring is not setup yet"));
94315516c77SSepherosa Ziehau 	if (hn_tx_swq_depth < txr->hn_txdesc_cnt)
94415516c77SSepherosa Ziehau 		return txr->hn_txdesc_cnt;
94515516c77SSepherosa Ziehau 	return hn_tx_swq_depth;
94615516c77SSepherosa Ziehau }
94715516c77SSepherosa Ziehau 
94834d68912SSepherosa Ziehau #ifndef RSS
94915516c77SSepherosa Ziehau static int
95015516c77SSepherosa Ziehau hn_rss_reconfig(struct hn_softc *sc)
95115516c77SSepherosa Ziehau {
95215516c77SSepherosa Ziehau 	int error;
95315516c77SSepherosa Ziehau 
95415516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
95515516c77SSepherosa Ziehau 
95615516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
95715516c77SSepherosa Ziehau 		return (ENXIO);
95815516c77SSepherosa Ziehau 
95915516c77SSepherosa Ziehau 	/*
96015516c77SSepherosa Ziehau 	 * Disable RSS first.
96115516c77SSepherosa Ziehau 	 *
96215516c77SSepherosa Ziehau 	 * NOTE:
96315516c77SSepherosa Ziehau 	 * Direct reconfiguration by setting the UNCHG flags does
96415516c77SSepherosa Ziehau 	 * _not_ work properly.
96515516c77SSepherosa Ziehau 	 */
96615516c77SSepherosa Ziehau 	if (bootverbose)
96715516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "disable RSS\n");
96815516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_DISABLE);
96915516c77SSepherosa Ziehau 	if (error) {
97015516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS disable failed\n");
97115516c77SSepherosa Ziehau 		return (error);
97215516c77SSepherosa Ziehau 	}
97315516c77SSepherosa Ziehau 
97415516c77SSepherosa Ziehau 	/*
97515516c77SSepherosa Ziehau 	 * Reenable the RSS w/ the updated RSS key or indirect
97615516c77SSepherosa Ziehau 	 * table.
97715516c77SSepherosa Ziehau 	 */
97815516c77SSepherosa Ziehau 	if (bootverbose)
97915516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "reconfig RSS\n");
98015516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
98115516c77SSepherosa Ziehau 	if (error) {
98215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS reconfig failed\n");
98315516c77SSepherosa Ziehau 		return (error);
98415516c77SSepherosa Ziehau 	}
98515516c77SSepherosa Ziehau 	return (0);
98615516c77SSepherosa Ziehau }
98734d68912SSepherosa Ziehau #endif	/* !RSS */
98815516c77SSepherosa Ziehau 
98915516c77SSepherosa Ziehau static void
990afd4971bSSepherosa Ziehau hn_rss_ind_fixup(struct hn_softc *sc)
99115516c77SSepherosa Ziehau {
99215516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
993afd4971bSSepherosa Ziehau 	int i, nchan;
99415516c77SSepherosa Ziehau 
995afd4971bSSepherosa Ziehau 	nchan = sc->hn_rx_ring_inuse;
99615516c77SSepherosa Ziehau 	KASSERT(nchan > 1, ("invalid # of channels %d", nchan));
99715516c77SSepherosa Ziehau 
99815516c77SSepherosa Ziehau 	/*
99915516c77SSepherosa Ziehau 	 * Check indirect table to make sure that all channels in it
100015516c77SSepherosa Ziehau 	 * can be used.
100115516c77SSepherosa Ziehau 	 */
100215516c77SSepherosa Ziehau 	for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
100315516c77SSepherosa Ziehau 		if (rss->rss_ind[i] >= nchan) {
100415516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp,
100515516c77SSepherosa Ziehau 			    "RSS indirect table %d fixup: %u -> %d\n",
100615516c77SSepherosa Ziehau 			    i, rss->rss_ind[i], nchan - 1);
100715516c77SSepherosa Ziehau 			rss->rss_ind[i] = nchan - 1;
100815516c77SSepherosa Ziehau 		}
100915516c77SSepherosa Ziehau 	}
101015516c77SSepherosa Ziehau }
101115516c77SSepherosa Ziehau 
101215516c77SSepherosa Ziehau static int
101315516c77SSepherosa Ziehau hn_ifmedia_upd(struct ifnet *ifp __unused)
101415516c77SSepherosa Ziehau {
101515516c77SSepherosa Ziehau 
101615516c77SSepherosa Ziehau 	return EOPNOTSUPP;
101715516c77SSepherosa Ziehau }
101815516c77SSepherosa Ziehau 
101915516c77SSepherosa Ziehau static void
102015516c77SSepherosa Ziehau hn_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
102115516c77SSepherosa Ziehau {
102215516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
102315516c77SSepherosa Ziehau 
102415516c77SSepherosa Ziehau 	ifmr->ifm_status = IFM_AVALID;
102515516c77SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
102615516c77SSepherosa Ziehau 
102715516c77SSepherosa Ziehau 	if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) {
102815516c77SSepherosa Ziehau 		ifmr->ifm_active |= IFM_NONE;
102915516c77SSepherosa Ziehau 		return;
103015516c77SSepherosa Ziehau 	}
103115516c77SSepherosa Ziehau 	ifmr->ifm_status |= IFM_ACTIVE;
103215516c77SSepherosa Ziehau 	ifmr->ifm_active |= IFM_10G_T | IFM_FDX;
103315516c77SSepherosa Ziehau }
103415516c77SSepherosa Ziehau 
10355bdfd3fdSDexuan Cui static void
1036962f0357SSepherosa Ziehau hn_rxvf_set_task(void *xarg, int pending __unused)
10375bdfd3fdSDexuan Cui {
1038962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg *arg = xarg;
10395bdfd3fdSDexuan Cui 
1040962f0357SSepherosa Ziehau 	arg->rxr->hn_rxvf_ifp = arg->vf_ifp;
10415bdfd3fdSDexuan Cui }
10425bdfd3fdSDexuan Cui 
10435bdfd3fdSDexuan Cui static void
1044962f0357SSepherosa Ziehau hn_rxvf_set(struct hn_softc *sc, struct ifnet *vf_ifp)
10455bdfd3fdSDexuan Cui {
10465bdfd3fdSDexuan Cui 	struct hn_rx_ring *rxr;
1047962f0357SSepherosa Ziehau 	struct hn_rxvf_setarg arg;
10485bdfd3fdSDexuan Cui 	struct task task;
10495bdfd3fdSDexuan Cui 	int i;
10505bdfd3fdSDexuan Cui 
10515bdfd3fdSDexuan Cui 	HN_LOCK_ASSERT(sc);
10525bdfd3fdSDexuan Cui 
1053962f0357SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_rxvf_set_task, &arg);
10545bdfd3fdSDexuan Cui 
10555bdfd3fdSDexuan Cui 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
10565bdfd3fdSDexuan Cui 		rxr = &sc->hn_rx_ring[i];
10575bdfd3fdSDexuan Cui 
10585bdfd3fdSDexuan Cui 		if (i < sc->hn_rx_ring_inuse) {
1059962f0357SSepherosa Ziehau 			arg.rxr = rxr;
1060962f0357SSepherosa Ziehau 			arg.vf_ifp = vf_ifp;
10615bdfd3fdSDexuan Cui 			vmbus_chan_run_task(rxr->hn_chan, &task);
10625bdfd3fdSDexuan Cui 		} else {
1063962f0357SSepherosa Ziehau 			rxr->hn_rxvf_ifp = vf_ifp;
10645bdfd3fdSDexuan Cui 		}
10655bdfd3fdSDexuan Cui 	}
10665bdfd3fdSDexuan Cui }
10675bdfd3fdSDexuan Cui 
1068962f0357SSepherosa Ziehau static bool
1069499c3e17SSepherosa Ziehau hn_ismyvf(const struct hn_softc *sc, const struct ifnet *ifp)
1070499c3e17SSepherosa Ziehau {
1071499c3e17SSepherosa Ziehau 	const struct ifnet *hn_ifp;
1072499c3e17SSepherosa Ziehau 
1073499c3e17SSepherosa Ziehau 	hn_ifp = sc->hn_ifp;
1074499c3e17SSepherosa Ziehau 
1075499c3e17SSepherosa Ziehau 	if (ifp == hn_ifp)
1076499c3e17SSepherosa Ziehau 		return (false);
1077499c3e17SSepherosa Ziehau 
1078499c3e17SSepherosa Ziehau 	if (ifp->if_alloctype != IFT_ETHER)
1079499c3e17SSepherosa Ziehau 		return (false);
1080499c3e17SSepherosa Ziehau 
1081499c3e17SSepherosa Ziehau 	/* Ignore lagg/vlan interfaces */
1082499c3e17SSepherosa Ziehau 	if (strcmp(ifp->if_dname, "lagg") == 0 ||
1083499c3e17SSepherosa Ziehau 	    strcmp(ifp->if_dname, "vlan") == 0)
1084499c3e17SSepherosa Ziehau 		return (false);
1085499c3e17SSepherosa Ziehau 
1086499c3e17SSepherosa Ziehau 	if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0)
1087499c3e17SSepherosa Ziehau 		return (false);
1088499c3e17SSepherosa Ziehau 
1089499c3e17SSepherosa Ziehau 	return (true);
1090499c3e17SSepherosa Ziehau }
1091499c3e17SSepherosa Ziehau 
10925bdfd3fdSDexuan Cui static void
1093962f0357SSepherosa Ziehau hn_rxvf_change(struct hn_softc *sc, struct ifnet *ifp, bool rxvf)
10945bdfd3fdSDexuan Cui {
10955bdfd3fdSDexuan Cui 	struct ifnet *hn_ifp;
10965bdfd3fdSDexuan Cui 
10975bdfd3fdSDexuan Cui 	HN_LOCK(sc);
10985bdfd3fdSDexuan Cui 
10995bdfd3fdSDexuan Cui 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
11005bdfd3fdSDexuan Cui 		goto out;
11015bdfd3fdSDexuan Cui 
1102499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1103499c3e17SSepherosa Ziehau 		goto out;
11045bdfd3fdSDexuan Cui 	hn_ifp = sc->hn_ifp;
11055bdfd3fdSDexuan Cui 
1106962f0357SSepherosa Ziehau 	if (rxvf) {
1107962f0357SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_RXVF)
11085bdfd3fdSDexuan Cui 			goto out;
11095bdfd3fdSDexuan Cui 
1110962f0357SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_RXVF;
11115bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
11125bdfd3fdSDexuan Cui 	} else {
1113962f0357SSepherosa Ziehau 		if (!(sc->hn_flags & HN_FLAG_RXVF))
11145bdfd3fdSDexuan Cui 			goto out;
11155bdfd3fdSDexuan Cui 
1116962f0357SSepherosa Ziehau 		sc->hn_flags &= ~HN_FLAG_RXVF;
1117499c3e17SSepherosa Ziehau 		if (hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
11185bdfd3fdSDexuan Cui 			hn_rxfilter_config(sc);
11195bdfd3fdSDexuan Cui 		else
11205bdfd3fdSDexuan Cui 			hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE);
11215bdfd3fdSDexuan Cui 	}
11225bdfd3fdSDexuan Cui 
11235bdfd3fdSDexuan Cui 	hn_nvs_set_datapath(sc,
11249c6cae24SSepherosa Ziehau 	    rxvf ? HN_NVS_DATAPATH_VF : HN_NVS_DATAPATH_SYNTH);
11255bdfd3fdSDexuan Cui 
1126962f0357SSepherosa Ziehau 	hn_rxvf_set(sc, rxvf ? ifp : NULL);
11275bdfd3fdSDexuan Cui 
1128962f0357SSepherosa Ziehau 	if (rxvf) {
11295bdfd3fdSDexuan Cui 		hn_suspend_mgmt(sc);
11305bdfd3fdSDexuan Cui 		sc->hn_link_flags &=
11315bdfd3fdSDexuan Cui 		    ~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG);
1132499c3e17SSepherosa Ziehau 		if_link_state_change(hn_ifp, LINK_STATE_DOWN);
11335bdfd3fdSDexuan Cui 	} else {
11345bdfd3fdSDexuan Cui 		hn_resume_mgmt(sc);
11355bdfd3fdSDexuan Cui 	}
11365bdfd3fdSDexuan Cui 
1137962f0357SSepherosa Ziehau 	devctl_notify("HYPERV_NIC_VF", hn_ifp->if_xname,
1138962f0357SSepherosa Ziehau 	    rxvf ? "VF_UP" : "VF_DOWN", NULL);
113933408a34SDexuan Cui 
1140962f0357SSepherosa Ziehau 	if (bootverbose) {
1141962f0357SSepherosa Ziehau 		if_printf(hn_ifp, "datapath is switched %s %s\n",
1142962f0357SSepherosa Ziehau 		    rxvf ? "to" : "from", ifp->if_xname);
1143962f0357SSepherosa Ziehau 	}
11445bdfd3fdSDexuan Cui out:
11455bdfd3fdSDexuan Cui 	HN_UNLOCK(sc);
11465bdfd3fdSDexuan Cui }
11475bdfd3fdSDexuan Cui 
11485bdfd3fdSDexuan Cui static void
11495bdfd3fdSDexuan Cui hn_ifnet_event(void *arg, struct ifnet *ifp, int event)
11505bdfd3fdSDexuan Cui {
1151962f0357SSepherosa Ziehau 
11525bdfd3fdSDexuan Cui 	if (event != IFNET_EVENT_UP && event != IFNET_EVENT_DOWN)
11535bdfd3fdSDexuan Cui 		return;
1154962f0357SSepherosa Ziehau 	hn_rxvf_change(arg, ifp, event == IFNET_EVENT_UP);
11555bdfd3fdSDexuan Cui }
11565bdfd3fdSDexuan Cui 
11575bdfd3fdSDexuan Cui static void
11585bdfd3fdSDexuan Cui hn_ifaddr_event(void *arg, struct ifnet *ifp)
11595bdfd3fdSDexuan Cui {
1160962f0357SSepherosa Ziehau 
1161962f0357SSepherosa Ziehau 	hn_rxvf_change(arg, ifp, ifp->if_flags & IFF_UP);
11625bdfd3fdSDexuan Cui }
11635bdfd3fdSDexuan Cui 
11649c6cae24SSepherosa Ziehau static int
11659c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetcaps(struct hn_softc *sc, struct ifreq *ifr)
11669c6cae24SSepherosa Ziehau {
11679c6cae24SSepherosa Ziehau 	struct ifnet *ifp, *vf_ifp;
11689c6cae24SSepherosa Ziehau 	uint64_t tmp;
11699c6cae24SSepherosa Ziehau 	int error;
11709c6cae24SSepherosa Ziehau 
11719c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
11729c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
11739c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
11749c6cae24SSepherosa Ziehau 
11759c6cae24SSepherosa Ziehau 	/*
11769c6cae24SSepherosa Ziehau 	 * Fix up requested capabilities w/ supported capabilities,
11779c6cae24SSepherosa Ziehau 	 * since the supported capabilities could have been changed.
11789c6cae24SSepherosa Ziehau 	 */
11799c6cae24SSepherosa Ziehau 	ifr->ifr_reqcap &= ifp->if_capabilities;
11809c6cae24SSepherosa Ziehau 	/* Pass SIOCSIFCAP to VF. */
11819c6cae24SSepherosa Ziehau 	error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFCAP, (caddr_t)ifr);
11829c6cae24SSepherosa Ziehau 
11839c6cae24SSepherosa Ziehau 	/*
11849c6cae24SSepherosa Ziehau 	 * NOTE:
11859c6cae24SSepherosa Ziehau 	 * The error will be propagated to the callers, however, it
11869c6cae24SSepherosa Ziehau 	 * is _not_ useful here.
11879c6cae24SSepherosa Ziehau 	 */
11889c6cae24SSepherosa Ziehau 
11899c6cae24SSepherosa Ziehau 	/*
11909c6cae24SSepherosa Ziehau 	 * Merge VF's enabled capabilities.
11919c6cae24SSepherosa Ziehau 	 */
11929c6cae24SSepherosa Ziehau 	ifp->if_capenable = vf_ifp->if_capenable & ifp->if_capabilities;
11939c6cae24SSepherosa Ziehau 
11949c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & HN_CSUM_IP_HWASSIST(sc);
11959c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TXCSUM)
11969c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
11979c6cae24SSepherosa Ziehau 	else
11989c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
11999c6cae24SSepherosa Ziehau 
12009c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & HN_CSUM_IP6_HWASSIST(sc);
12019c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
12029c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
12039c6cae24SSepherosa Ziehau 	else
12049c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
12059c6cae24SSepherosa Ziehau 
12069c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & CSUM_IP_TSO;
12079c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TSO4)
12089c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
12099c6cae24SSepherosa Ziehau 	else
12109c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
12119c6cae24SSepherosa Ziehau 
12129c6cae24SSepherosa Ziehau 	tmp = vf_ifp->if_hwassist & CSUM_IP6_TSO;
12139c6cae24SSepherosa Ziehau 	if (ifp->if_capenable & IFCAP_TSO6)
12149c6cae24SSepherosa Ziehau 		ifp->if_hwassist |= tmp;
12159c6cae24SSepherosa Ziehau 	else
12169c6cae24SSepherosa Ziehau 		ifp->if_hwassist &= ~tmp;
12179c6cae24SSepherosa Ziehau 
12189c6cae24SSepherosa Ziehau 	return (error);
12199c6cae24SSepherosa Ziehau }
12209c6cae24SSepherosa Ziehau 
12219c6cae24SSepherosa Ziehau static int
12229c6cae24SSepherosa Ziehau hn_xpnt_vf_iocsetflags(struct hn_softc *sc)
12239c6cae24SSepherosa Ziehau {
12249c6cae24SSepherosa Ziehau 	struct ifnet *vf_ifp;
12259c6cae24SSepherosa Ziehau 	struct ifreq ifr;
12269c6cae24SSepherosa Ziehau 
12279c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
12289c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
12299c6cae24SSepherosa Ziehau 
12309c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
12319c6cae24SSepherosa Ziehau 	strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
12329c6cae24SSepherosa Ziehau 	ifr.ifr_flags = vf_ifp->if_flags & 0xffff;
12339c6cae24SSepherosa Ziehau 	ifr.ifr_flagshigh = vf_ifp->if_flags >> 16;
12349c6cae24SSepherosa Ziehau 	return (vf_ifp->if_ioctl(vf_ifp, SIOCSIFFLAGS, (caddr_t)&ifr));
12359c6cae24SSepherosa Ziehau }
12369c6cae24SSepherosa Ziehau 
12379c6cae24SSepherosa Ziehau static void
12389c6cae24SSepherosa Ziehau hn_xpnt_vf_saveifflags(struct hn_softc *sc)
12399c6cae24SSepherosa Ziehau {
12409c6cae24SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
12419c6cae24SSepherosa Ziehau 	int allmulti = 0;
12429c6cae24SSepherosa Ziehau 
12439c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
12449c6cae24SSepherosa Ziehau 
12459c6cae24SSepherosa Ziehau 	/* XXX vlan(4) style mcast addr maintenance */
12469c6cae24SSepherosa Ziehau 	if (!TAILQ_EMPTY(&ifp->if_multiaddrs))
12479c6cae24SSepherosa Ziehau 		allmulti = IFF_ALLMULTI;
12489c6cae24SSepherosa Ziehau 
12499c6cae24SSepherosa Ziehau 	/* Always set the VF's if_flags */
12509c6cae24SSepherosa Ziehau 	sc->hn_vf_ifp->if_flags = ifp->if_flags | allmulti;
12519c6cae24SSepherosa Ziehau }
12529c6cae24SSepherosa Ziehau 
12539c6cae24SSepherosa Ziehau static void
12549c6cae24SSepherosa Ziehau hn_xpnt_vf_input(struct ifnet *vf_ifp, struct mbuf *m)
12559c6cae24SSepherosa Ziehau {
12569c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
12579c6cae24SSepherosa Ziehau 	struct ifnet *hn_ifp = NULL;
12589c6cae24SSepherosa Ziehau 	struct mbuf *mn;
12599c6cae24SSepherosa Ziehau 
12609c6cae24SSepherosa Ziehau 	/*
12619c6cae24SSepherosa Ziehau 	 * XXX racy, if hn(4) ever detached.
12629c6cae24SSepherosa Ziehau 	 */
12639c6cae24SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
12649c6cae24SSepherosa Ziehau 	if (vf_ifp->if_index < hn_vfmap_size)
12659c6cae24SSepherosa Ziehau 		hn_ifp = hn_vfmap[vf_ifp->if_index];
12669c6cae24SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
12679c6cae24SSepherosa Ziehau 
12689c6cae24SSepherosa Ziehau 	if (hn_ifp != NULL) {
12699c6cae24SSepherosa Ziehau 		for (mn = m; mn != NULL; mn = mn->m_nextpkt) {
1270*3bed4e54SSepherosa Ziehau 			/*
1271*3bed4e54SSepherosa Ziehau 			 * Allow tapping on the VF.
1272*3bed4e54SSepherosa Ziehau 			 */
12739c6cae24SSepherosa Ziehau 			ETHER_BPF_MTAP(vf_ifp, mn);
1274*3bed4e54SSepherosa Ziehau 
1275*3bed4e54SSepherosa Ziehau 			/*
1276*3bed4e54SSepherosa Ziehau 			 * Update VF stats.
1277*3bed4e54SSepherosa Ziehau 			 */
1278*3bed4e54SSepherosa Ziehau 			if ((vf_ifp->if_capenable & IFCAP_HWSTATS) == 0) {
1279*3bed4e54SSepherosa Ziehau 				if_inc_counter(vf_ifp, IFCOUNTER_IBYTES,
1280*3bed4e54SSepherosa Ziehau 				    mn->m_pkthdr.len);
1281*3bed4e54SSepherosa Ziehau 			}
1282*3bed4e54SSepherosa Ziehau 			/*
1283*3bed4e54SSepherosa Ziehau 			 * XXX IFCOUNTER_IMCAST
1284*3bed4e54SSepherosa Ziehau 			 * This stat updating is kinda invasive, since it
1285*3bed4e54SSepherosa Ziehau 			 * requires two checks on the mbuf: the length check
1286*3bed4e54SSepherosa Ziehau 			 * and the ethernet header check.  As of this write,
1287*3bed4e54SSepherosa Ziehau 			 * all multicast packets go directly to hn(4), which
1288*3bed4e54SSepherosa Ziehau 			 * makes imcast stat updating in the VF a try in vian.
1289*3bed4e54SSepherosa Ziehau 			 */
1290*3bed4e54SSepherosa Ziehau 
1291*3bed4e54SSepherosa Ziehau 			/*
1292*3bed4e54SSepherosa Ziehau 			 * Fix up rcvif and increase hn(4)'s ipackets.
1293*3bed4e54SSepherosa Ziehau 			 */
12949c6cae24SSepherosa Ziehau 			mn->m_pkthdr.rcvif = hn_ifp;
12959c6cae24SSepherosa Ziehau 			if_inc_counter(hn_ifp, IFCOUNTER_IPACKETS, 1);
12969c6cae24SSepherosa Ziehau 		}
1297*3bed4e54SSepherosa Ziehau 		/*
1298*3bed4e54SSepherosa Ziehau 		 * Go through hn(4)'s if_input.
1299*3bed4e54SSepherosa Ziehau 		 */
13009c6cae24SSepherosa Ziehau 		hn_ifp->if_input(hn_ifp, m);
13019c6cae24SSepherosa Ziehau 	} else {
13029c6cae24SSepherosa Ziehau 		/*
13039c6cae24SSepherosa Ziehau 		 * In the middle of the transition; free this
13049c6cae24SSepherosa Ziehau 		 * mbuf chain.
13059c6cae24SSepherosa Ziehau 		 */
13069c6cae24SSepherosa Ziehau 		while (m != NULL) {
13079c6cae24SSepherosa Ziehau 			mn = m->m_nextpkt;
13089c6cae24SSepherosa Ziehau 			m->m_nextpkt = NULL;
13099c6cae24SSepherosa Ziehau 			m_freem(m);
13109c6cae24SSepherosa Ziehau 			m = mn;
13119c6cae24SSepherosa Ziehau 		}
13129c6cae24SSepherosa Ziehau 	}
13139c6cae24SSepherosa Ziehau }
13149c6cae24SSepherosa Ziehau 
13159c6cae24SSepherosa Ziehau static void
13169c6cae24SSepherosa Ziehau hn_mtu_change_fixup(struct hn_softc *sc)
13179c6cae24SSepherosa Ziehau {
13189c6cae24SSepherosa Ziehau 	struct ifnet *ifp;
13199c6cae24SSepherosa Ziehau 
13209c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13219c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
13229c6cae24SSepherosa Ziehau 
13239c6cae24SSepherosa Ziehau 	hn_set_tso_maxsize(sc, hn_tso_maxlen, ifp->if_mtu);
13249c6cae24SSepherosa Ziehau #if __FreeBSD_version >= 1100099
13259c6cae24SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_lro.lro_length_lim < HN_LRO_LENLIM_MIN(ifp))
13269c6cae24SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp));
13279c6cae24SSepherosa Ziehau #endif
13289c6cae24SSepherosa Ziehau }
13299c6cae24SSepherosa Ziehau 
13309c6cae24SSepherosa Ziehau static void
13319c6cae24SSepherosa Ziehau hn_xpnt_vf_setready(struct hn_softc *sc)
13329c6cae24SSepherosa Ziehau {
13339c6cae24SSepherosa Ziehau 	struct ifnet *ifp, *vf_ifp;
13349c6cae24SSepherosa Ziehau 	struct ifreq ifr;
13359c6cae24SSepherosa Ziehau 
13369c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
13379c6cae24SSepherosa Ziehau 	ifp = sc->hn_ifp;
13389c6cae24SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
13399c6cae24SSepherosa Ziehau 
13409c6cae24SSepherosa Ziehau 	/*
13419c6cae24SSepherosa Ziehau 	 * Mark the VF ready.
13429c6cae24SSepherosa Ziehau 	 */
13439c6cae24SSepherosa Ziehau 	sc->hn_vf_rdytick = 0;
13449c6cae24SSepherosa Ziehau 
13459c6cae24SSepherosa Ziehau 	/*
13469c6cae24SSepherosa Ziehau 	 * Save information for restoration.
13479c6cae24SSepherosa Ziehau 	 */
13489c6cae24SSepherosa Ziehau 	sc->hn_saved_caps = ifp->if_capabilities;
13499c6cae24SSepherosa Ziehau 	sc->hn_saved_tsomax = ifp->if_hw_tsomax;
13509c6cae24SSepherosa Ziehau 	sc->hn_saved_tsosegcnt = ifp->if_hw_tsomaxsegcount;
13519c6cae24SSepherosa Ziehau 	sc->hn_saved_tsosegsz = ifp->if_hw_tsomaxsegsize;
13529c6cae24SSepherosa Ziehau 
13539c6cae24SSepherosa Ziehau 	/*
13549c6cae24SSepherosa Ziehau 	 * Intersect supported/enabled capabilities.
13559c6cae24SSepherosa Ziehau 	 *
13569c6cae24SSepherosa Ziehau 	 * NOTE:
13579c6cae24SSepherosa Ziehau 	 * if_hwassist is not changed here.
13589c6cae24SSepherosa Ziehau 	 */
13599c6cae24SSepherosa Ziehau 	ifp->if_capabilities &= vf_ifp->if_capabilities;
13609c6cae24SSepherosa Ziehau 	ifp->if_capenable &= ifp->if_capabilities;
13619c6cae24SSepherosa Ziehau 
13629c6cae24SSepherosa Ziehau 	/*
13639c6cae24SSepherosa Ziehau 	 * Fix TSO settings.
13649c6cae24SSepherosa Ziehau 	 */
13659c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomax > vf_ifp->if_hw_tsomax)
13669c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomax = vf_ifp->if_hw_tsomax;
13679c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomaxsegcount > vf_ifp->if_hw_tsomaxsegcount)
13689c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomaxsegcount = vf_ifp->if_hw_tsomaxsegcount;
13699c6cae24SSepherosa Ziehau 	if (ifp->if_hw_tsomaxsegsize > vf_ifp->if_hw_tsomaxsegsize)
13709c6cae24SSepherosa Ziehau 		ifp->if_hw_tsomaxsegsize = vf_ifp->if_hw_tsomaxsegsize;
13719c6cae24SSepherosa Ziehau 
13729c6cae24SSepherosa Ziehau 	/*
13739c6cae24SSepherosa Ziehau 	 * Change VF's enabled capabilities.
13749c6cae24SSepherosa Ziehau 	 */
13759c6cae24SSepherosa Ziehau 	memset(&ifr, 0, sizeof(ifr));
13769c6cae24SSepherosa Ziehau 	strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
13779c6cae24SSepherosa Ziehau 	ifr.ifr_reqcap = ifp->if_capenable;
13789c6cae24SSepherosa Ziehau 	hn_xpnt_vf_iocsetcaps(sc, &ifr);
13799c6cae24SSepherosa Ziehau 
13809c6cae24SSepherosa Ziehau 	if (ifp->if_mtu != ETHERMTU) {
13819c6cae24SSepherosa Ziehau 		int error;
13829c6cae24SSepherosa Ziehau 
13839c6cae24SSepherosa Ziehau 		/*
13849c6cae24SSepherosa Ziehau 		 * Change VF's MTU.
13859c6cae24SSepherosa Ziehau 		 */
13869c6cae24SSepherosa Ziehau 		memset(&ifr, 0, sizeof(ifr));
13879c6cae24SSepherosa Ziehau 		strlcpy(ifr.ifr_name, vf_ifp->if_xname, sizeof(ifr.ifr_name));
13889c6cae24SSepherosa Ziehau 		ifr.ifr_mtu = ifp->if_mtu;
13899c6cae24SSepherosa Ziehau 		error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFMTU, (caddr_t)&ifr);
13909c6cae24SSepherosa Ziehau 		if (error) {
13919c6cae24SSepherosa Ziehau 			if_printf(ifp, "%s SIOCSIFMTU %u failed\n",
13929c6cae24SSepherosa Ziehau 			    vf_ifp->if_xname, ifp->if_mtu);
13939c6cae24SSepherosa Ziehau 			if (ifp->if_mtu > ETHERMTU) {
13949c6cae24SSepherosa Ziehau 				if_printf(ifp, "change MTU to %d\n", ETHERMTU);
13959c6cae24SSepherosa Ziehau 
13969c6cae24SSepherosa Ziehau 				/*
13979c6cae24SSepherosa Ziehau 				 * XXX
13989c6cae24SSepherosa Ziehau 				 * No need to adjust the synthetic parts' MTU;
13999c6cae24SSepherosa Ziehau 				 * failure of the adjustment will cause us
14009c6cae24SSepherosa Ziehau 				 * infinite headache.
14019c6cae24SSepherosa Ziehau 				 */
14029c6cae24SSepherosa Ziehau 				ifp->if_mtu = ETHERMTU;
14039c6cae24SSepherosa Ziehau 				hn_mtu_change_fixup(sc);
14049c6cae24SSepherosa Ziehau 			}
14059c6cae24SSepherosa Ziehau 		}
14069c6cae24SSepherosa Ziehau 	}
14079c6cae24SSepherosa Ziehau }
14089c6cae24SSepherosa Ziehau 
14099c6cae24SSepherosa Ziehau static bool
14109c6cae24SSepherosa Ziehau hn_xpnt_vf_isready(struct hn_softc *sc)
14119c6cae24SSepherosa Ziehau {
14129c6cae24SSepherosa Ziehau 
14139c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
14149c6cae24SSepherosa Ziehau 
14159c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf || sc->hn_vf_ifp == NULL)
14169c6cae24SSepherosa Ziehau 		return (false);
14179c6cae24SSepherosa Ziehau 
14189c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick == 0)
14199c6cae24SSepherosa Ziehau 		return (true);
14209c6cae24SSepherosa Ziehau 
14219c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick > ticks)
14229c6cae24SSepherosa Ziehau 		return (false);
14239c6cae24SSepherosa Ziehau 
14249c6cae24SSepherosa Ziehau 	/* Mark VF as ready. */
14259c6cae24SSepherosa Ziehau 	hn_xpnt_vf_setready(sc);
14269c6cae24SSepherosa Ziehau 	return (true);
14279c6cae24SSepherosa Ziehau }
14289c6cae24SSepherosa Ziehau 
14299c6cae24SSepherosa Ziehau static void
14309c6cae24SSepherosa Ziehau hn_xpnt_vf_init(struct hn_softc *sc)
14319c6cae24SSepherosa Ziehau {
14329c6cae24SSepherosa Ziehau 	int error;
14339c6cae24SSepherosa Ziehau 
14349c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
14359c6cae24SSepherosa Ziehau 
14369c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
14379c6cae24SSepherosa Ziehau 	    ("%s: transparent VF was enabled", sc->hn_ifp->if_xname));
14389c6cae24SSepherosa Ziehau 
14399c6cae24SSepherosa Ziehau 	if (bootverbose) {
14409c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "try bringing up %s\n",
14419c6cae24SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname);
14429c6cae24SSepherosa Ziehau 	}
14439c6cae24SSepherosa Ziehau 
14449c6cae24SSepherosa Ziehau 	/*
14459c6cae24SSepherosa Ziehau 	 * Bring the VF up.
14469c6cae24SSepherosa Ziehau 	 */
14479c6cae24SSepherosa Ziehau 	hn_xpnt_vf_saveifflags(sc);
14489c6cae24SSepherosa Ziehau 	sc->hn_vf_ifp->if_flags |= IFF_UP;
14499c6cae24SSepherosa Ziehau 	error = hn_xpnt_vf_iocsetflags(sc);
14509c6cae24SSepherosa Ziehau 	if (error) {
14519c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "bringing up %s failed: %d\n",
14529c6cae24SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname, error);
14539c6cae24SSepherosa Ziehau 		return;
14549c6cae24SSepherosa Ziehau 	}
14559c6cae24SSepherosa Ziehau 
14569c6cae24SSepherosa Ziehau 	/*
14579c6cae24SSepherosa Ziehau 	 * NOTE:
14589c6cae24SSepherosa Ziehau 	 * Datapath setting must happen _after_ bringing the VF up.
14599c6cae24SSepherosa Ziehau 	 */
14609c6cae24SSepherosa Ziehau 	hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
14619c6cae24SSepherosa Ziehau 
14629c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
14639c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
14649c6cae24SSepherosa Ziehau 	sc->hn_xvf_flags |= HN_XVFFLAG_ENABLED;
14659c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
14669c6cae24SSepherosa Ziehau }
14679c6cae24SSepherosa Ziehau 
14689c6cae24SSepherosa Ziehau static void
14699c6cae24SSepherosa Ziehau hn_xpnt_vf_init_taskfunc(void *xsc, int pending __unused)
14709c6cae24SSepherosa Ziehau {
14719c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
14729c6cae24SSepherosa Ziehau 
14739c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
14749c6cae24SSepherosa Ziehau 
14759c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
14769c6cae24SSepherosa Ziehau 		goto done;
14779c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
14789c6cae24SSepherosa Ziehau 		goto done;
14799c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
14809c6cae24SSepherosa Ziehau 		goto done;
14819c6cae24SSepherosa Ziehau 
14829c6cae24SSepherosa Ziehau 	if (sc->hn_vf_rdytick != 0) {
14839c6cae24SSepherosa Ziehau 		/* Mark VF as ready. */
14849c6cae24SSepherosa Ziehau 		hn_xpnt_vf_setready(sc);
14859c6cae24SSepherosa Ziehau 	}
14869c6cae24SSepherosa Ziehau 
14879c6cae24SSepherosa Ziehau 	if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) {
14889c6cae24SSepherosa Ziehau 		/*
14899c6cae24SSepherosa Ziehau 		 * Delayed VF initialization.
14909c6cae24SSepherosa Ziehau 		 */
14919c6cae24SSepherosa Ziehau 		if (bootverbose) {
14929c6cae24SSepherosa Ziehau 			if_printf(sc->hn_ifp, "delayed initialize %s\n",
14939c6cae24SSepherosa Ziehau 			    sc->hn_vf_ifp->if_xname);
14949c6cae24SSepherosa Ziehau 		}
14959c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
14969c6cae24SSepherosa Ziehau 	}
14979c6cae24SSepherosa Ziehau done:
14989c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
14999c6cae24SSepherosa Ziehau }
15009c6cae24SSepherosa Ziehau 
1501499c3e17SSepherosa Ziehau static void
1502499c3e17SSepherosa Ziehau hn_ifnet_attevent(void *xsc, struct ifnet *ifp)
1503499c3e17SSepherosa Ziehau {
1504499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1505499c3e17SSepherosa Ziehau 
1506499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1507499c3e17SSepherosa Ziehau 
1508499c3e17SSepherosa Ziehau 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
1509499c3e17SSepherosa Ziehau 		goto done;
1510499c3e17SSepherosa Ziehau 
1511499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1512499c3e17SSepherosa Ziehau 		goto done;
1513499c3e17SSepherosa Ziehau 
1514499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp != NULL) {
1515499c3e17SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s was attached as VF\n",
1516499c3e17SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname);
1517499c3e17SSepherosa Ziehau 		goto done;
1518499c3e17SSepherosa Ziehau 	}
1519499c3e17SSepherosa Ziehau 
15209c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && ifp->if_start != NULL) {
15219c6cae24SSepherosa Ziehau 		/*
15229c6cae24SSepherosa Ziehau 		 * ifnet.if_start is _not_ supported by transparent
15239c6cae24SSepherosa Ziehau 		 * mode VF; mainly due to the IFF_DRV_OACTIVE flag.
15249c6cae24SSepherosa Ziehau 		 */
15259c6cae24SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s uses if_start, which is unsupported "
15269c6cae24SSepherosa Ziehau 		    "in transparent VF mode.\n", ifp->if_xname);
15279c6cae24SSepherosa Ziehau 		goto done;
15289c6cae24SSepherosa Ziehau 	}
15299c6cae24SSepherosa Ziehau 
1530499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
1531499c3e17SSepherosa Ziehau 
1532499c3e17SSepherosa Ziehau 	if (ifp->if_index >= hn_vfmap_size) {
1533499c3e17SSepherosa Ziehau 		struct ifnet **newmap;
1534499c3e17SSepherosa Ziehau 		int newsize;
1535499c3e17SSepherosa Ziehau 
1536499c3e17SSepherosa Ziehau 		newsize = ifp->if_index + HN_VFMAP_SIZE_DEF;
1537499c3e17SSepherosa Ziehau 		newmap = malloc(sizeof(struct ifnet *) * newsize, M_DEVBUF,
1538499c3e17SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
1539499c3e17SSepherosa Ziehau 
1540499c3e17SSepherosa Ziehau 		memcpy(newmap, hn_vfmap,
1541499c3e17SSepherosa Ziehau 		    sizeof(struct ifnet *) * hn_vfmap_size);
1542499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
1543499c3e17SSepherosa Ziehau 		hn_vfmap = newmap;
1544499c3e17SSepherosa Ziehau 		hn_vfmap_size = newsize;
1545499c3e17SSepherosa Ziehau 	}
1546499c3e17SSepherosa Ziehau 	KASSERT(hn_vfmap[ifp->if_index] == NULL,
1547499c3e17SSepherosa Ziehau 	    ("%s: ifindex %d was mapped to %s",
1548499c3e17SSepherosa Ziehau 	     ifp->if_xname, ifp->if_index, hn_vfmap[ifp->if_index]->if_xname));
1549499c3e17SSepherosa Ziehau 	hn_vfmap[ifp->if_index] = sc->hn_ifp;
1550499c3e17SSepherosa Ziehau 
1551499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
1552499c3e17SSepherosa Ziehau 
15539c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
15549c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
15559c6cae24SSepherosa Ziehau 	KASSERT((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) == 0,
15569c6cae24SSepherosa Ziehau 	    ("%s: transparent VF was enabled", sc->hn_ifp->if_xname));
1557499c3e17SSepherosa Ziehau 	sc->hn_vf_ifp = ifp;
15589c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
15599c6cae24SSepherosa Ziehau 
15609c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
15619c6cae24SSepherosa Ziehau 		int wait_ticks;
15629c6cae24SSepherosa Ziehau 
15639c6cae24SSepherosa Ziehau 		/*
15649c6cae24SSepherosa Ziehau 		 * Install if_input for vf_ifp, which does vf_ifp -> hn_ifp.
15659c6cae24SSepherosa Ziehau 		 * Save vf_ifp's current if_input for later restoration.
15669c6cae24SSepherosa Ziehau 		 */
15679c6cae24SSepherosa Ziehau 		sc->hn_vf_input = ifp->if_input;
15689c6cae24SSepherosa Ziehau 		ifp->if_input = hn_xpnt_vf_input;
15699c6cae24SSepherosa Ziehau 
15709c6cae24SSepherosa Ziehau 		/*
15719c6cae24SSepherosa Ziehau 		 * Stop link status management; use the VF's.
15729c6cae24SSepherosa Ziehau 		 */
15739c6cae24SSepherosa Ziehau 		hn_suspend_mgmt(sc);
15749c6cae24SSepherosa Ziehau 
15759c6cae24SSepherosa Ziehau 		/*
15769c6cae24SSepherosa Ziehau 		 * Give VF sometime to complete its attach routing.
15779c6cae24SSepherosa Ziehau 		 */
15789c6cae24SSepherosa Ziehau 		wait_ticks = hn_xpnt_vf_attwait * hz;
15799c6cae24SSepherosa Ziehau 		sc->hn_vf_rdytick = ticks + wait_ticks;
15809c6cae24SSepherosa Ziehau 
15819c6cae24SSepherosa Ziehau 		taskqueue_enqueue_timeout(sc->hn_vf_taskq, &sc->hn_vf_init,
15829c6cae24SSepherosa Ziehau 		    wait_ticks);
15839c6cae24SSepherosa Ziehau 	}
1584499c3e17SSepherosa Ziehau done:
1585499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
1586499c3e17SSepherosa Ziehau }
1587499c3e17SSepherosa Ziehau 
1588499c3e17SSepherosa Ziehau static void
1589499c3e17SSepherosa Ziehau hn_ifnet_detevent(void *xsc, struct ifnet *ifp)
1590499c3e17SSepherosa Ziehau {
1591499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1592499c3e17SSepherosa Ziehau 
1593499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1594499c3e17SSepherosa Ziehau 
1595499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
1596499c3e17SSepherosa Ziehau 		goto done;
1597499c3e17SSepherosa Ziehau 
1598499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1599499c3e17SSepherosa Ziehau 		goto done;
1600499c3e17SSepherosa Ziehau 
16019c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
16029c6cae24SSepherosa Ziehau 		/*
16039c6cae24SSepherosa Ziehau 		 * Make sure that the delayed initialization is not running.
16049c6cae24SSepherosa Ziehau 		 *
16059c6cae24SSepherosa Ziehau 		 * NOTE:
16069c6cae24SSepherosa Ziehau 		 * - This lock _must_ be released, since the hn_vf_init task
16079c6cae24SSepherosa Ziehau 		 *   will try holding this lock.
16089c6cae24SSepherosa Ziehau 		 * - It is safe to release this lock here, since the
16099c6cae24SSepherosa Ziehau 		 *   hn_ifnet_attevent() is interlocked by the hn_vf_ifp.
16109c6cae24SSepherosa Ziehau 		 *
16119c6cae24SSepherosa Ziehau 		 * XXX racy, if hn(4) ever detached.
16129c6cae24SSepherosa Ziehau 		 */
16139c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
16149c6cae24SSepherosa Ziehau 		taskqueue_drain_timeout(sc->hn_vf_taskq, &sc->hn_vf_init);
16159c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
16169c6cae24SSepherosa Ziehau 
16179c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_input != NULL, ("%s VF input is not saved",
16189c6cae24SSepherosa Ziehau 		    sc->hn_ifp->if_xname));
16199c6cae24SSepherosa Ziehau 		ifp->if_input = sc->hn_vf_input;
16209c6cae24SSepherosa Ziehau 		sc->hn_vf_input = NULL;
16219c6cae24SSepherosa Ziehau 
16229c6cae24SSepherosa Ziehau 		if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
16239c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
16249c6cae24SSepherosa Ziehau 
16259c6cae24SSepherosa Ziehau 		if (sc->hn_vf_rdytick == 0) {
16269c6cae24SSepherosa Ziehau 			/*
16279c6cae24SSepherosa Ziehau 			 * The VF was ready; restore some settings.
16289c6cae24SSepherosa Ziehau 			 */
16299c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_capabilities = sc->hn_saved_caps;
16309c6cae24SSepherosa Ziehau 			/*
16319c6cae24SSepherosa Ziehau 			 * NOTE:
16329c6cae24SSepherosa Ziehau 			 * There is _no_ need to fixup if_capenable and
16339c6cae24SSepherosa Ziehau 			 * if_hwassist, since the if_capabilities before
16349c6cae24SSepherosa Ziehau 			 * restoration was an intersection of the VF's
16359c6cae24SSepherosa Ziehau 			 * if_capabilites and the synthetic device's
16369c6cae24SSepherosa Ziehau 			 * if_capabilites.
16379c6cae24SSepherosa Ziehau 			 */
16389c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomax = sc->hn_saved_tsomax;
16399c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomaxsegcount =
16409c6cae24SSepherosa Ziehau 			    sc->hn_saved_tsosegcnt;
16419c6cae24SSepherosa Ziehau 			sc->hn_ifp->if_hw_tsomaxsegsize = sc->hn_saved_tsosegsz;
16429c6cae24SSepherosa Ziehau 		}
16439c6cae24SSepherosa Ziehau 
16449c6cae24SSepherosa Ziehau 		/*
16459c6cae24SSepherosa Ziehau 		 * Resume link status management, which was suspended
16469c6cae24SSepherosa Ziehau 		 * by hn_ifnet_attevent().
16479c6cae24SSepherosa Ziehau 		 */
16489c6cae24SSepherosa Ziehau 		hn_resume_mgmt(sc);
16499c6cae24SSepherosa Ziehau 	}
16509c6cae24SSepherosa Ziehau 
16519c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit()/hn_qflush() */
16529c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
16539c6cae24SSepherosa Ziehau 	sc->hn_xvf_flags &= ~HN_XVFFLAG_ENABLED;
1654499c3e17SSepherosa Ziehau 	sc->hn_vf_ifp = NULL;
16559c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
1656499c3e17SSepherosa Ziehau 
1657499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
1658499c3e17SSepherosa Ziehau 
1659499c3e17SSepherosa Ziehau 	KASSERT(ifp->if_index < hn_vfmap_size,
1660499c3e17SSepherosa Ziehau 	    ("ifindex %d, vfmapsize %d", ifp->if_index, hn_vfmap_size));
1661499c3e17SSepherosa Ziehau 	if (hn_vfmap[ifp->if_index] != NULL) {
1662499c3e17SSepherosa Ziehau 		KASSERT(hn_vfmap[ifp->if_index] == sc->hn_ifp,
1663499c3e17SSepherosa Ziehau 		    ("%s: ifindex %d was mapped to %s",
1664499c3e17SSepherosa Ziehau 		     ifp->if_xname, ifp->if_index,
1665499c3e17SSepherosa Ziehau 		     hn_vfmap[ifp->if_index]->if_xname));
1666499c3e17SSepherosa Ziehau 		hn_vfmap[ifp->if_index] = NULL;
1667499c3e17SSepherosa Ziehau 	}
1668499c3e17SSepherosa Ziehau 
1669499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
1670499c3e17SSepherosa Ziehau done:
1671499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
1672499c3e17SSepherosa Ziehau }
1673499c3e17SSepherosa Ziehau 
16749c6cae24SSepherosa Ziehau static void
16759c6cae24SSepherosa Ziehau hn_ifnet_lnkevent(void *xsc, struct ifnet *ifp, int link_state)
16769c6cae24SSepherosa Ziehau {
16779c6cae24SSepherosa Ziehau 	struct hn_softc *sc = xsc;
16789c6cae24SSepherosa Ziehau 
16799c6cae24SSepherosa Ziehau 	if (sc->hn_vf_ifp == ifp)
16809c6cae24SSepherosa Ziehau 		if_link_state_change(sc->hn_ifp, link_state);
16819c6cae24SSepherosa Ziehau }
16829c6cae24SSepherosa Ziehau 
168315516c77SSepherosa Ziehau /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
168415516c77SSepherosa Ziehau static const struct hyperv_guid g_net_vsc_device_type = {
168515516c77SSepherosa Ziehau 	.hv_guid = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
168615516c77SSepherosa Ziehau 		0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}
168715516c77SSepherosa Ziehau };
168815516c77SSepherosa Ziehau 
168915516c77SSepherosa Ziehau static int
169015516c77SSepherosa Ziehau hn_probe(device_t dev)
169115516c77SSepherosa Ziehau {
169215516c77SSepherosa Ziehau 
169315516c77SSepherosa Ziehau 	if (VMBUS_PROBE_GUID(device_get_parent(dev), dev,
169415516c77SSepherosa Ziehau 	    &g_net_vsc_device_type) == 0) {
169515516c77SSepherosa Ziehau 		device_set_desc(dev, "Hyper-V Network Interface");
169615516c77SSepherosa Ziehau 		return BUS_PROBE_DEFAULT;
169715516c77SSepherosa Ziehau 	}
169815516c77SSepherosa Ziehau 	return ENXIO;
169915516c77SSepherosa Ziehau }
170015516c77SSepherosa Ziehau 
170115516c77SSepherosa Ziehau static int
170215516c77SSepherosa Ziehau hn_attach(device_t dev)
170315516c77SSepherosa Ziehau {
170415516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
170515516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
170615516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
170715516c77SSepherosa Ziehau 	uint8_t eaddr[ETHER_ADDR_LEN];
170815516c77SSepherosa Ziehau 	struct ifnet *ifp = NULL;
170915516c77SSepherosa Ziehau 	int error, ring_cnt, tx_ring_cnt;
171015516c77SSepherosa Ziehau 
171115516c77SSepherosa Ziehau 	sc->hn_dev = dev;
171215516c77SSepherosa Ziehau 	sc->hn_prichan = vmbus_get_channel(dev);
171315516c77SSepherosa Ziehau 	HN_LOCK_INIT(sc);
17149c6cae24SSepherosa Ziehau 	rm_init(&sc->hn_vf_lock, "hnvf");
17159c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_xpnt_vf_accbpf)
17169c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
171715516c77SSepherosa Ziehau 
171815516c77SSepherosa Ziehau 	/*
1719dc13fee6SSepherosa Ziehau 	 * Initialize these tunables once.
1720dc13fee6SSepherosa Ziehau 	 */
1721dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = hn_tx_agg_size;
1722dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = hn_tx_agg_pkts;
1723dc13fee6SSepherosa Ziehau 
1724dc13fee6SSepherosa Ziehau 	/*
172515516c77SSepherosa Ziehau 	 * Setup taskqueue for transmission.
172615516c77SSepherosa Ziehau 	 */
17270e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_INDEP) {
1728fdd0222aSSepherosa Ziehau 		int i;
1729fdd0222aSSepherosa Ziehau 
1730fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs =
1731fdd0222aSSepherosa Ziehau 		    malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
1732fdd0222aSSepherosa Ziehau 		    M_DEVBUF, M_WAITOK);
1733fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i) {
1734fdd0222aSSepherosa Ziehau 			sc->hn_tx_taskqs[i] = taskqueue_create("hn_tx",
1735fdd0222aSSepherosa Ziehau 			    M_WAITOK, taskqueue_thread_enqueue,
1736fdd0222aSSepherosa Ziehau 			    &sc->hn_tx_taskqs[i]);
1737fdd0222aSSepherosa Ziehau 			taskqueue_start_threads(&sc->hn_tx_taskqs[i], 1, PI_NET,
1738fdd0222aSSepherosa Ziehau 			    "%s tx%d", device_get_nameunit(dev), i);
1739fdd0222aSSepherosa Ziehau 		}
17400e11868dSSepherosa Ziehau 	} else if (hn_tx_taskq_mode == HN_TX_TASKQ_M_GLOBAL) {
1741fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs = hn_tx_taskque;
174215516c77SSepherosa Ziehau 	}
174315516c77SSepherosa Ziehau 
174415516c77SSepherosa Ziehau 	/*
174515516c77SSepherosa Ziehau 	 * Setup taskqueue for mangement tasks, e.g. link status.
174615516c77SSepherosa Ziehau 	 */
174715516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq0 = taskqueue_create("hn_mgmt", M_WAITOK,
174815516c77SSepherosa Ziehau 	    taskqueue_thread_enqueue, &sc->hn_mgmt_taskq0);
174915516c77SSepherosa Ziehau 	taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
175015516c77SSepherosa Ziehau 	    device_get_nameunit(dev));
175115516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc);
175215516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_netchg_init, 0, hn_netchg_init_taskfunc, sc);
175315516c77SSepherosa Ziehau 	TIMEOUT_TASK_INIT(sc->hn_mgmt_taskq0, &sc->hn_netchg_status, 0,
175415516c77SSepherosa Ziehau 	    hn_netchg_status_taskfunc, sc);
175515516c77SSepherosa Ziehau 
17569c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf) {
17579c6cae24SSepherosa Ziehau 		/*
17589c6cae24SSepherosa Ziehau 		 * Setup taskqueue for VF tasks, e.g. delayed VF bringing up.
17599c6cae24SSepherosa Ziehau 		 */
17609c6cae24SSepherosa Ziehau 		sc->hn_vf_taskq = taskqueue_create("hn_vf", M_WAITOK,
17619c6cae24SSepherosa Ziehau 		    taskqueue_thread_enqueue, &sc->hn_vf_taskq);
17629c6cae24SSepherosa Ziehau 		taskqueue_start_threads(&sc->hn_vf_taskq, 1, PI_NET, "%s vf",
17639c6cae24SSepherosa Ziehau 		    device_get_nameunit(dev));
17649c6cae24SSepherosa Ziehau 		TIMEOUT_TASK_INIT(sc->hn_vf_taskq, &sc->hn_vf_init, 0,
17659c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_init_taskfunc, sc);
17669c6cae24SSepherosa Ziehau 	}
17679c6cae24SSepherosa Ziehau 
176815516c77SSepherosa Ziehau 	/*
176915516c77SSepherosa Ziehau 	 * Allocate ifnet and setup its name earlier, so that if_printf
177015516c77SSepherosa Ziehau 	 * can be used by functions, which will be called after
177115516c77SSepherosa Ziehau 	 * ether_ifattach().
177215516c77SSepherosa Ziehau 	 */
177315516c77SSepherosa Ziehau 	ifp = sc->hn_ifp = if_alloc(IFT_ETHER);
177415516c77SSepherosa Ziehau 	ifp->if_softc = sc;
177515516c77SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
177615516c77SSepherosa Ziehau 
177715516c77SSepherosa Ziehau 	/*
177815516c77SSepherosa Ziehau 	 * Initialize ifmedia earlier so that it can be unconditionally
177915516c77SSepherosa Ziehau 	 * destroyed, if error happened later on.
178015516c77SSepherosa Ziehau 	 */
178115516c77SSepherosa Ziehau 	ifmedia_init(&sc->hn_media, 0, hn_ifmedia_upd, hn_ifmedia_sts);
178215516c77SSepherosa Ziehau 
178315516c77SSepherosa Ziehau 	/*
178415516c77SSepherosa Ziehau 	 * Figure out the # of RX rings (ring_cnt) and the # of TX rings
178515516c77SSepherosa Ziehau 	 * to use (tx_ring_cnt).
178615516c77SSepherosa Ziehau 	 *
178715516c77SSepherosa Ziehau 	 * NOTE:
178815516c77SSepherosa Ziehau 	 * The # of RX rings to use is same as the # of channels to use.
178915516c77SSepherosa Ziehau 	 */
179015516c77SSepherosa Ziehau 	ring_cnt = hn_chan_cnt;
179115516c77SSepherosa Ziehau 	if (ring_cnt <= 0) {
179215516c77SSepherosa Ziehau 		/* Default */
179315516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
179415516c77SSepherosa Ziehau 		if (ring_cnt > HN_RING_CNT_DEF_MAX)
179515516c77SSepherosa Ziehau 			ring_cnt = HN_RING_CNT_DEF_MAX;
179615516c77SSepherosa Ziehau 	} else if (ring_cnt > mp_ncpus) {
179715516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
179815516c77SSepherosa Ziehau 	}
179934d68912SSepherosa Ziehau #ifdef RSS
180034d68912SSepherosa Ziehau 	if (ring_cnt > rss_getnumbuckets())
180134d68912SSepherosa Ziehau 		ring_cnt = rss_getnumbuckets();
180234d68912SSepherosa Ziehau #endif
180315516c77SSepherosa Ziehau 
180415516c77SSepherosa Ziehau 	tx_ring_cnt = hn_tx_ring_cnt;
180515516c77SSepherosa Ziehau 	if (tx_ring_cnt <= 0 || tx_ring_cnt > ring_cnt)
180615516c77SSepherosa Ziehau 		tx_ring_cnt = ring_cnt;
180723bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
180815516c77SSepherosa Ziehau 	if (hn_use_if_start) {
180915516c77SSepherosa Ziehau 		/* ifnet.if_start only needs one TX ring. */
181015516c77SSepherosa Ziehau 		tx_ring_cnt = 1;
181115516c77SSepherosa Ziehau 	}
181223bf9e15SSepherosa Ziehau #endif
181315516c77SSepherosa Ziehau 
181415516c77SSepherosa Ziehau 	/*
181515516c77SSepherosa Ziehau 	 * Set the leader CPU for channels.
181615516c77SSepherosa Ziehau 	 */
181715516c77SSepherosa Ziehau 	sc->hn_cpu = atomic_fetchadd_int(&hn_cpu_index, ring_cnt) % mp_ncpus;
181815516c77SSepherosa Ziehau 
181915516c77SSepherosa Ziehau 	/*
182015516c77SSepherosa Ziehau 	 * Create enough TX/RX rings, even if only limited number of
182115516c77SSepherosa Ziehau 	 * channels can be allocated.
182215516c77SSepherosa Ziehau 	 */
182315516c77SSepherosa Ziehau 	error = hn_create_tx_data(sc, tx_ring_cnt);
182415516c77SSepherosa Ziehau 	if (error)
182515516c77SSepherosa Ziehau 		goto failed;
182615516c77SSepherosa Ziehau 	error = hn_create_rx_data(sc, ring_cnt);
182715516c77SSepherosa Ziehau 	if (error)
182815516c77SSepherosa Ziehau 		goto failed;
182915516c77SSepherosa Ziehau 
183015516c77SSepherosa Ziehau 	/*
183115516c77SSepherosa Ziehau 	 * Create transaction context for NVS and RNDIS transactions.
183215516c77SSepherosa Ziehau 	 */
183315516c77SSepherosa Ziehau 	sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
183415516c77SSepherosa Ziehau 	    HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
183525641fc7SSepherosa Ziehau 	if (sc->hn_xact == NULL) {
183625641fc7SSepherosa Ziehau 		error = ENXIO;
183715516c77SSepherosa Ziehau 		goto failed;
183825641fc7SSepherosa Ziehau 	}
183925641fc7SSepherosa Ziehau 
184025641fc7SSepherosa Ziehau 	/*
184125641fc7SSepherosa Ziehau 	 * Install orphan handler for the revocation of this device's
184225641fc7SSepherosa Ziehau 	 * primary channel.
184325641fc7SSepherosa Ziehau 	 *
184425641fc7SSepherosa Ziehau 	 * NOTE:
184525641fc7SSepherosa Ziehau 	 * The processing order is critical here:
184625641fc7SSepherosa Ziehau 	 * Install the orphan handler, _before_ testing whether this
184725641fc7SSepherosa Ziehau 	 * device's primary channel has been revoked or not.
184825641fc7SSepherosa Ziehau 	 */
184925641fc7SSepherosa Ziehau 	vmbus_chan_set_orphan(sc->hn_prichan, sc->hn_xact);
185025641fc7SSepherosa Ziehau 	if (vmbus_chan_is_revoked(sc->hn_prichan)) {
185125641fc7SSepherosa Ziehau 		error = ENXIO;
185225641fc7SSepherosa Ziehau 		goto failed;
185325641fc7SSepherosa Ziehau 	}
185415516c77SSepherosa Ziehau 
185515516c77SSepherosa Ziehau 	/*
185615516c77SSepherosa Ziehau 	 * Attach the synthetic parts, i.e. NVS and RNDIS.
185715516c77SSepherosa Ziehau 	 */
185815516c77SSepherosa Ziehau 	error = hn_synth_attach(sc, ETHERMTU);
185915516c77SSepherosa Ziehau 	if (error)
186015516c77SSepherosa Ziehau 		goto failed;
186115516c77SSepherosa Ziehau 
186215516c77SSepherosa Ziehau 	error = hn_rndis_get_eaddr(sc, eaddr);
186315516c77SSepherosa Ziehau 	if (error)
186415516c77SSepherosa Ziehau 		goto failed;
186515516c77SSepherosa Ziehau 
186615516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
186715516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
186815516c77SSepherosa Ziehau 		/*
186915516c77SSepherosa Ziehau 		 * Reduce TCP segment aggregation limit for multiple
187015516c77SSepherosa Ziehau 		 * RX rings to increase ACK timeliness.
187115516c77SSepherosa Ziehau 		 */
187215516c77SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF);
187315516c77SSepherosa Ziehau 	}
187415516c77SSepherosa Ziehau #endif
187515516c77SSepherosa Ziehau 
187615516c77SSepherosa Ziehau 	/*
187715516c77SSepherosa Ziehau 	 * Fixup TX stuffs after synthetic parts are attached.
187815516c77SSepherosa Ziehau 	 */
187915516c77SSepherosa Ziehau 	hn_fixup_tx_data(sc);
188015516c77SSepherosa Ziehau 
188115516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
188215516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
188315516c77SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "nvs_version", CTLFLAG_RD,
188415516c77SSepherosa Ziehau 	    &sc->hn_nvs_ver, 0, "NVS version");
188515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ndis_version",
188615516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
188715516c77SSepherosa Ziehau 	    hn_ndis_version_sysctl, "A", "NDIS version");
188815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "caps",
188915516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
189015516c77SSepherosa Ziehau 	    hn_caps_sysctl, "A", "capabilities");
189115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist",
189215516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
189315516c77SSepherosa Ziehau 	    hn_hwassist_sysctl, "A", "hwassist");
18949c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_max",
18959c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomax, 0, "max TSO size");
18969c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_maxsegcnt",
18979c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomaxsegcount, 0,
18989c6cae24SSepherosa Ziehau 	    "max # of TSO segments");
18999c6cae24SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tso_maxsegsz",
19009c6cae24SSepherosa Ziehau 	    CTLFLAG_RD, &ifp->if_hw_tsomaxsegsize, 0,
19019c6cae24SSepherosa Ziehau 	    "max size of TSO segment");
190215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter",
190315516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
190415516c77SSepherosa Ziehau 	    hn_rxfilter_sysctl, "A", "rxfilter");
190515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hash",
190615516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
190715516c77SSepherosa Ziehau 	    hn_rss_hash_sysctl, "A", "RSS hash");
190815516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rss_ind_size",
190915516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rss_ind_size, 0, "RSS indirect entry count");
191034d68912SSepherosa Ziehau #ifndef RSS
191134d68912SSepherosa Ziehau 	/*
191234d68912SSepherosa Ziehau 	 * Don't allow RSS key/indirect table changes, if RSS is defined.
191334d68912SSepherosa Ziehau 	 */
191415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key",
191515516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
191615516c77SSepherosa Ziehau 	    hn_rss_key_sysctl, "IU", "RSS key");
191715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_ind",
191815516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
191915516c77SSepherosa Ziehau 	    hn_rss_ind_sysctl, "IU", "RSS indirect table");
192034d68912SSepherosa Ziehau #endif
1921dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_size",
1922dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_size, 0,
1923dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation size limit");
1924dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_pkts",
1925dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_pkts, 0,
1926dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation count limit");
1927dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_align",
1928dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_align, 0,
1929dc13fee6SSepherosa Ziehau 	    "RNDIS packet transmission aggregation alignment");
1930dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_size",
1931dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
1932dc13fee6SSepherosa Ziehau 	    hn_txagg_size_sysctl, "I",
1933dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation size, 0 -- disable, -1 -- auto");
1934dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pkts",
1935dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
1936dc13fee6SSepherosa Ziehau 	    hn_txagg_pkts_sysctl, "I",
1937dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation packets, "
1938dc13fee6SSepherosa Ziehau 	    "0 -- disable, -1 -- auto");
19396c1204dfSSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "polling",
19406c1204dfSSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
19416c1204dfSSepherosa Ziehau 	    hn_polling_sysctl, "I",
19426c1204dfSSepherosa Ziehau 	    "Polling frequency: [100,1000000], 0 disable polling");
194340d60d6eSDexuan Cui 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf",
194440d60d6eSDexuan Cui 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
194540d60d6eSDexuan Cui 	    hn_vf_sysctl, "A", "Virtual Function's name");
19469c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
1947499c3e17SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxvf",
1948499c3e17SSepherosa Ziehau 		    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
1949499c3e17SSepherosa Ziehau 		    hn_rxvf_sysctl, "A", "activated Virtual Function's name");
19509c6cae24SSepherosa Ziehau 	} else {
19519c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_enabled",
19529c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
19539c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_enabled_sysctl, "I",
19549c6cae24SSepherosa Ziehau 		    "Transparent VF enabled");
19559c6cae24SSepherosa Ziehau 		SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf_xpnt_accbpf",
19569c6cae24SSepherosa Ziehau 		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
19579c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_accbpf_sysctl, "I",
19589c6cae24SSepherosa Ziehau 		    "Accurate BPF for transparent VF");
19599c6cae24SSepherosa Ziehau 	}
196015516c77SSepherosa Ziehau 
196115516c77SSepherosa Ziehau 	/*
196215516c77SSepherosa Ziehau 	 * Setup the ifmedia, which has been initialized earlier.
196315516c77SSepherosa Ziehau 	 */
196415516c77SSepherosa Ziehau 	ifmedia_add(&sc->hn_media, IFM_ETHER | IFM_AUTO, 0, NULL);
196515516c77SSepherosa Ziehau 	ifmedia_set(&sc->hn_media, IFM_ETHER | IFM_AUTO);
196615516c77SSepherosa Ziehau 	/* XXX ifmedia_set really should do this for us */
196715516c77SSepherosa Ziehau 	sc->hn_media.ifm_media = sc->hn_media.ifm_cur->ifm_media;
196815516c77SSepherosa Ziehau 
196915516c77SSepherosa Ziehau 	/*
197015516c77SSepherosa Ziehau 	 * Setup the ifnet for this interface.
197115516c77SSepherosa Ziehau 	 */
197215516c77SSepherosa Ziehau 
197315516c77SSepherosa Ziehau 	ifp->if_baudrate = IF_Gbps(10);
197415516c77SSepherosa Ziehau 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
197515516c77SSepherosa Ziehau 	ifp->if_ioctl = hn_ioctl;
197615516c77SSepherosa Ziehau 	ifp->if_init = hn_init;
197723bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
197815516c77SSepherosa Ziehau 	if (hn_use_if_start) {
197915516c77SSepherosa Ziehau 		int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]);
198015516c77SSepherosa Ziehau 
198115516c77SSepherosa Ziehau 		ifp->if_start = hn_start;
198215516c77SSepherosa Ziehau 		IFQ_SET_MAXLEN(&ifp->if_snd, qdepth);
198315516c77SSepherosa Ziehau 		ifp->if_snd.ifq_drv_maxlen = qdepth - 1;
198415516c77SSepherosa Ziehau 		IFQ_SET_READY(&ifp->if_snd);
198523bf9e15SSepherosa Ziehau 	} else
198623bf9e15SSepherosa Ziehau #endif
198723bf9e15SSepherosa Ziehau 	{
198815516c77SSepherosa Ziehau 		ifp->if_transmit = hn_transmit;
198915516c77SSepherosa Ziehau 		ifp->if_qflush = hn_xmit_qflush;
199015516c77SSepherosa Ziehau 	}
199115516c77SSepherosa Ziehau 
19929c6cae24SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_LRO | IFCAP_LINKSTATE;
199315516c77SSepherosa Ziehau #ifdef foo
199415516c77SSepherosa Ziehau 	/* We can't diff IPv6 packets from IPv4 packets on RX path. */
199515516c77SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_RXCSUM_IPV6;
199615516c77SSepherosa Ziehau #endif
199715516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_VLAN) {
199815516c77SSepherosa Ziehau 		/* XXX not sure about VLAN_MTU. */
199915516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
200015516c77SSepherosa Ziehau 	}
200115516c77SSepherosa Ziehau 
200215516c77SSepherosa Ziehau 	ifp->if_hwassist = sc->hn_tx_ring[0].hn_csum_assist;
200315516c77SSepherosa Ziehau 	if (ifp->if_hwassist & HN_CSUM_IP_MASK)
200415516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TXCSUM;
200515516c77SSepherosa Ziehau 	if (ifp->if_hwassist & HN_CSUM_IP6_MASK)
200615516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TXCSUM_IPV6;
200715516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO4) {
200815516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TSO4;
200915516c77SSepherosa Ziehau 		ifp->if_hwassist |= CSUM_IP_TSO;
201015516c77SSepherosa Ziehau 	}
201115516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO6) {
201215516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TSO6;
201315516c77SSepherosa Ziehau 		ifp->if_hwassist |= CSUM_IP6_TSO;
201415516c77SSepherosa Ziehau 	}
201515516c77SSepherosa Ziehau 
201615516c77SSepherosa Ziehau 	/* Enable all available capabilities by default. */
201715516c77SSepherosa Ziehau 	ifp->if_capenable = ifp->if_capabilities;
201815516c77SSepherosa Ziehau 
20197960e6baSSepherosa Ziehau 	/*
20207960e6baSSepherosa Ziehau 	 * Disable IPv6 TSO and TXCSUM by default, they still can
20217960e6baSSepherosa Ziehau 	 * be enabled through SIOCSIFCAP.
20227960e6baSSepherosa Ziehau 	 */
20237960e6baSSepherosa Ziehau 	ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6 | IFCAP_TSO6);
20247960e6baSSepherosa Ziehau 	ifp->if_hwassist &= ~(HN_CSUM_IP6_MASK | CSUM_IP6_TSO);
20257960e6baSSepherosa Ziehau 
202615516c77SSepherosa Ziehau 	if (ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) {
20279c6cae24SSepherosa Ziehau 		/*
20289c6cae24SSepherosa Ziehau 		 * Lock hn_set_tso_maxsize() to simplify its
20299c6cae24SSepherosa Ziehau 		 * internal logic.
20309c6cae24SSepherosa Ziehau 		 */
20319c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
203215516c77SSepherosa Ziehau 		hn_set_tso_maxsize(sc, hn_tso_maxlen, ETHERMTU);
20339c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
203415516c77SSepherosa Ziehau 		ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX;
203515516c77SSepherosa Ziehau 		ifp->if_hw_tsomaxsegsize = PAGE_SIZE;
203615516c77SSepherosa Ziehau 	}
203715516c77SSepherosa Ziehau 
203815516c77SSepherosa Ziehau 	ether_ifattach(ifp, eaddr);
203915516c77SSepherosa Ziehau 
204015516c77SSepherosa Ziehau 	if ((ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) && bootverbose) {
204115516c77SSepherosa Ziehau 		if_printf(ifp, "TSO segcnt %u segsz %u\n",
204215516c77SSepherosa Ziehau 		    ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize);
204315516c77SSepherosa Ziehau 	}
204415516c77SSepherosa Ziehau 
204515516c77SSepherosa Ziehau 	/* Inform the upper layer about the long frame support. */
204615516c77SSepherosa Ziehau 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
204715516c77SSepherosa Ziehau 
204815516c77SSepherosa Ziehau 	/*
204915516c77SSepherosa Ziehau 	 * Kick off link status check.
205015516c77SSepherosa Ziehau 	 */
205115516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
205215516c77SSepherosa Ziehau 	hn_update_link_status(sc);
205315516c77SSepherosa Ziehau 
20549c6cae24SSepherosa Ziehau 	if (!hn_xpnt_vf) {
20555bdfd3fdSDexuan Cui 		sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event,
20565bdfd3fdSDexuan Cui 		    hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY);
20575bdfd3fdSDexuan Cui 		sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event,
20585bdfd3fdSDexuan Cui 		    hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY);
20599c6cae24SSepherosa Ziehau 	} else {
20609c6cae24SSepherosa Ziehau 		sc->hn_ifnet_lnkhand = EVENTHANDLER_REGISTER(ifnet_link_event,
20619c6cae24SSepherosa Ziehau 		    hn_ifnet_lnkevent, sc, EVENTHANDLER_PRI_ANY);
20629c6cae24SSepherosa Ziehau 	}
20635bdfd3fdSDexuan Cui 
2064f41e0df4SSepherosa Ziehau 	/*
2065f41e0df4SSepherosa Ziehau 	 * NOTE:
2066f41e0df4SSepherosa Ziehau 	 * Subscribe ether_ifattach event, instead of ifnet_arrival event,
2067f41e0df4SSepherosa Ziehau 	 * since interface's LLADDR is needed; interface LLADDR is not
2068f41e0df4SSepherosa Ziehau 	 * available when ifnet_arrival event is triggered.
2069f41e0df4SSepherosa Ziehau 	 */
2070499c3e17SSepherosa Ziehau 	sc->hn_ifnet_atthand = EVENTHANDLER_REGISTER(ether_ifattach_event,
2071499c3e17SSepherosa Ziehau 	    hn_ifnet_attevent, sc, EVENTHANDLER_PRI_ANY);
2072499c3e17SSepherosa Ziehau 	sc->hn_ifnet_dethand = EVENTHANDLER_REGISTER(ifnet_departure_event,
2073499c3e17SSepherosa Ziehau 	    hn_ifnet_detevent, sc, EVENTHANDLER_PRI_ANY);
2074499c3e17SSepherosa Ziehau 
207515516c77SSepherosa Ziehau 	return (0);
207615516c77SSepherosa Ziehau failed:
207715516c77SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
207815516c77SSepherosa Ziehau 		hn_synth_detach(sc);
207915516c77SSepherosa Ziehau 	hn_detach(dev);
208015516c77SSepherosa Ziehau 	return (error);
208115516c77SSepherosa Ziehau }
208215516c77SSepherosa Ziehau 
208315516c77SSepherosa Ziehau static int
208415516c77SSepherosa Ziehau hn_detach(device_t dev)
208515516c77SSepherosa Ziehau {
208615516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
2087499c3e17SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp, *vf_ifp;
208815516c77SSepherosa Ziehau 
20899c6cae24SSepherosa Ziehau 	if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
20909c6cae24SSepherosa Ziehau 		/*
20919c6cae24SSepherosa Ziehau 		 * In case that the vmbus missed the orphan handler
20929c6cae24SSepherosa Ziehau 		 * installation.
20939c6cae24SSepherosa Ziehau 		 */
20949c6cae24SSepherosa Ziehau 		vmbus_xact_ctx_orphan(sc->hn_xact);
20959c6cae24SSepherosa Ziehau 	}
20969c6cae24SSepherosa Ziehau 
20975bdfd3fdSDexuan Cui 	if (sc->hn_ifaddr_evthand != NULL)
20985bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand);
20995bdfd3fdSDexuan Cui 	if (sc->hn_ifnet_evthand != NULL)
21005bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand);
2101499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_atthand != NULL) {
2102499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ether_ifattach_event,
2103499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_atthand);
2104499c3e17SSepherosa Ziehau 	}
2105499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_dethand != NULL) {
2106499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
2107499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_dethand);
2108499c3e17SSepherosa Ziehau 	}
21099c6cae24SSepherosa Ziehau 	if (sc->hn_ifnet_lnkhand != NULL)
21109c6cae24SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_link_event, sc->hn_ifnet_lnkhand);
2111499c3e17SSepherosa Ziehau 
2112499c3e17SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
2113499c3e17SSepherosa Ziehau 	__compiler_membar();
2114499c3e17SSepherosa Ziehau 	if (vf_ifp != NULL)
2115499c3e17SSepherosa Ziehau 		hn_ifnet_detevent(sc, vf_ifp);
21165bdfd3fdSDexuan Cui 
211715516c77SSepherosa Ziehau 	if (device_is_attached(dev)) {
211815516c77SSepherosa Ziehau 		HN_LOCK(sc);
211915516c77SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
212015516c77SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
21215bdfd3fdSDexuan Cui 				hn_stop(sc, true);
212215516c77SSepherosa Ziehau 			/*
212315516c77SSepherosa Ziehau 			 * NOTE:
212415516c77SSepherosa Ziehau 			 * hn_stop() only suspends data, so managment
212515516c77SSepherosa Ziehau 			 * stuffs have to be suspended manually here.
212615516c77SSepherosa Ziehau 			 */
212715516c77SSepherosa Ziehau 			hn_suspend_mgmt(sc);
212815516c77SSepherosa Ziehau 			hn_synth_detach(sc);
212915516c77SSepherosa Ziehau 		}
213015516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
213115516c77SSepherosa Ziehau 		ether_ifdetach(ifp);
213215516c77SSepherosa Ziehau 	}
213315516c77SSepherosa Ziehau 
213415516c77SSepherosa Ziehau 	ifmedia_removeall(&sc->hn_media);
213515516c77SSepherosa Ziehau 	hn_destroy_rx_data(sc);
213615516c77SSepherosa Ziehau 	hn_destroy_tx_data(sc);
213715516c77SSepherosa Ziehau 
21380e11868dSSepherosa Ziehau 	if (sc->hn_tx_taskqs != NULL && sc->hn_tx_taskqs != hn_tx_taskque) {
2139fdd0222aSSepherosa Ziehau 		int i;
2140fdd0222aSSepherosa Ziehau 
2141fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
2142fdd0222aSSepherosa Ziehau 			taskqueue_free(sc->hn_tx_taskqs[i]);
2143fdd0222aSSepherosa Ziehau 		free(sc->hn_tx_taskqs, M_DEVBUF);
2144fdd0222aSSepherosa Ziehau 	}
214515516c77SSepherosa Ziehau 	taskqueue_free(sc->hn_mgmt_taskq0);
21469c6cae24SSepherosa Ziehau 	if (sc->hn_vf_taskq != NULL)
21479c6cae24SSepherosa Ziehau 		taskqueue_free(sc->hn_vf_taskq);
214815516c77SSepherosa Ziehau 
214925641fc7SSepherosa Ziehau 	if (sc->hn_xact != NULL) {
215025641fc7SSepherosa Ziehau 		/*
215125641fc7SSepherosa Ziehau 		 * Uninstall the orphan handler _before_ the xact is
215225641fc7SSepherosa Ziehau 		 * destructed.
215325641fc7SSepherosa Ziehau 		 */
215425641fc7SSepherosa Ziehau 		vmbus_chan_unset_orphan(sc->hn_prichan);
215515516c77SSepherosa Ziehau 		vmbus_xact_ctx_destroy(sc->hn_xact);
215625641fc7SSepherosa Ziehau 	}
215715516c77SSepherosa Ziehau 
215815516c77SSepherosa Ziehau 	if_free(ifp);
215915516c77SSepherosa Ziehau 
216015516c77SSepherosa Ziehau 	HN_LOCK_DESTROY(sc);
21619c6cae24SSepherosa Ziehau 	rm_destroy(&sc->hn_vf_lock);
216215516c77SSepherosa Ziehau 	return (0);
216315516c77SSepherosa Ziehau }
216415516c77SSepherosa Ziehau 
216515516c77SSepherosa Ziehau static int
216615516c77SSepherosa Ziehau hn_shutdown(device_t dev)
216715516c77SSepherosa Ziehau {
216815516c77SSepherosa Ziehau 
216915516c77SSepherosa Ziehau 	return (0);
217015516c77SSepherosa Ziehau }
217115516c77SSepherosa Ziehau 
217215516c77SSepherosa Ziehau static void
217315516c77SSepherosa Ziehau hn_link_status(struct hn_softc *sc)
217415516c77SSepherosa Ziehau {
217515516c77SSepherosa Ziehau 	uint32_t link_status;
217615516c77SSepherosa Ziehau 	int error;
217715516c77SSepherosa Ziehau 
217815516c77SSepherosa Ziehau 	error = hn_rndis_get_linkstatus(sc, &link_status);
217915516c77SSepherosa Ziehau 	if (error) {
218015516c77SSepherosa Ziehau 		/* XXX what to do? */
218115516c77SSepherosa Ziehau 		return;
218215516c77SSepherosa Ziehau 	}
218315516c77SSepherosa Ziehau 
218415516c77SSepherosa Ziehau 	if (link_status == NDIS_MEDIA_STATE_CONNECTED)
218515516c77SSepherosa Ziehau 		sc->hn_link_flags |= HN_LINK_FLAG_LINKUP;
218615516c77SSepherosa Ziehau 	else
218715516c77SSepherosa Ziehau 		sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
218815516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp,
218915516c77SSepherosa Ziehau 	    (sc->hn_link_flags & HN_LINK_FLAG_LINKUP) ?
219015516c77SSepherosa Ziehau 	    LINK_STATE_UP : LINK_STATE_DOWN);
219115516c77SSepherosa Ziehau }
219215516c77SSepherosa Ziehau 
219315516c77SSepherosa Ziehau static void
219415516c77SSepherosa Ziehau hn_link_taskfunc(void *xsc, int pending __unused)
219515516c77SSepherosa Ziehau {
219615516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
219715516c77SSepherosa Ziehau 
219815516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
219915516c77SSepherosa Ziehau 		return;
220015516c77SSepherosa Ziehau 	hn_link_status(sc);
220115516c77SSepherosa Ziehau }
220215516c77SSepherosa Ziehau 
220315516c77SSepherosa Ziehau static void
220415516c77SSepherosa Ziehau hn_netchg_init_taskfunc(void *xsc, int pending __unused)
220515516c77SSepherosa Ziehau {
220615516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
220715516c77SSepherosa Ziehau 
220815516c77SSepherosa Ziehau 	/* Prevent any link status checks from running. */
220915516c77SSepherosa Ziehau 	sc->hn_link_flags |= HN_LINK_FLAG_NETCHG;
221015516c77SSepherosa Ziehau 
221115516c77SSepherosa Ziehau 	/*
221215516c77SSepherosa Ziehau 	 * Fake up a [link down --> link up] state change; 5 seconds
221315516c77SSepherosa Ziehau 	 * delay is used, which closely simulates miibus reaction
221415516c77SSepherosa Ziehau 	 * upon link down event.
221515516c77SSepherosa Ziehau 	 */
221615516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
221715516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
221815516c77SSepherosa Ziehau 	taskqueue_enqueue_timeout(sc->hn_mgmt_taskq0,
221915516c77SSepherosa Ziehau 	    &sc->hn_netchg_status, 5 * hz);
222015516c77SSepherosa Ziehau }
222115516c77SSepherosa Ziehau 
222215516c77SSepherosa Ziehau static void
222315516c77SSepherosa Ziehau hn_netchg_status_taskfunc(void *xsc, int pending __unused)
222415516c77SSepherosa Ziehau {
222515516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
222615516c77SSepherosa Ziehau 
222715516c77SSepherosa Ziehau 	/* Re-allow link status checks. */
222815516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_NETCHG;
222915516c77SSepherosa Ziehau 	hn_link_status(sc);
223015516c77SSepherosa Ziehau }
223115516c77SSepherosa Ziehau 
223215516c77SSepherosa Ziehau static void
223315516c77SSepherosa Ziehau hn_update_link_status(struct hn_softc *sc)
223415516c77SSepherosa Ziehau {
223515516c77SSepherosa Ziehau 
223615516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
223715516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
223815516c77SSepherosa Ziehau }
223915516c77SSepherosa Ziehau 
224015516c77SSepherosa Ziehau static void
224115516c77SSepherosa Ziehau hn_change_network(struct hn_softc *sc)
224215516c77SSepherosa Ziehau {
224315516c77SSepherosa Ziehau 
224415516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
224515516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_netchg_init);
224615516c77SSepherosa Ziehau }
224715516c77SSepherosa Ziehau 
224815516c77SSepherosa Ziehau static __inline int
224915516c77SSepherosa Ziehau hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
225015516c77SSepherosa Ziehau     struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
225115516c77SSepherosa Ziehau {
225215516c77SSepherosa Ziehau 	struct mbuf *m = *m_head;
225315516c77SSepherosa Ziehau 	int error;
225415516c77SSepherosa Ziehau 
225515516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID, ("txd uses chim"));
225615516c77SSepherosa Ziehau 
225715516c77SSepherosa Ziehau 	error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag, txd->data_dmap,
225815516c77SSepherosa Ziehau 	    m, segs, nsegs, BUS_DMA_NOWAIT);
225915516c77SSepherosa Ziehau 	if (error == EFBIG) {
226015516c77SSepherosa Ziehau 		struct mbuf *m_new;
226115516c77SSepherosa Ziehau 
226215516c77SSepherosa Ziehau 		m_new = m_collapse(m, M_NOWAIT, HN_TX_DATA_SEGCNT_MAX);
226315516c77SSepherosa Ziehau 		if (m_new == NULL)
226415516c77SSepherosa Ziehau 			return ENOBUFS;
226515516c77SSepherosa Ziehau 		else
226615516c77SSepherosa Ziehau 			*m_head = m = m_new;
226715516c77SSepherosa Ziehau 		txr->hn_tx_collapsed++;
226815516c77SSepherosa Ziehau 
226915516c77SSepherosa Ziehau 		error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag,
227015516c77SSepherosa Ziehau 		    txd->data_dmap, m, segs, nsegs, BUS_DMA_NOWAIT);
227115516c77SSepherosa Ziehau 	}
227215516c77SSepherosa Ziehau 	if (!error) {
227315516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag, txd->data_dmap,
227415516c77SSepherosa Ziehau 		    BUS_DMASYNC_PREWRITE);
227515516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_DMAMAP;
227615516c77SSepherosa Ziehau 	}
227715516c77SSepherosa Ziehau 	return error;
227815516c77SSepherosa Ziehau }
227915516c77SSepherosa Ziehau 
228015516c77SSepherosa Ziehau static __inline int
228115516c77SSepherosa Ziehau hn_txdesc_put(struct hn_tx_ring *txr, struct hn_txdesc *txd)
228215516c77SSepherosa Ziehau {
228315516c77SSepherosa Ziehau 
228415516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONLIST) == 0,
228515516c77SSepherosa Ziehau 	    ("put an onlist txd %#x", txd->flags));
2286dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2287dc13fee6SSepherosa Ziehau 	    ("put an onagg txd %#x", txd->flags));
228815516c77SSepherosa Ziehau 
228915516c77SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
229015516c77SSepherosa Ziehau 	if (atomic_fetchadd_int(&txd->refs, -1) != 1)
229115516c77SSepherosa Ziehau 		return 0;
229215516c77SSepherosa Ziehau 
2293dc13fee6SSepherosa Ziehau 	if (!STAILQ_EMPTY(&txd->agg_list)) {
2294dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tmp_txd;
2295dc13fee6SSepherosa Ziehau 
2296dc13fee6SSepherosa Ziehau 		while ((tmp_txd = STAILQ_FIRST(&txd->agg_list)) != NULL) {
2297dc13fee6SSepherosa Ziehau 			int freed;
2298dc13fee6SSepherosa Ziehau 
2299dc13fee6SSepherosa Ziehau 			KASSERT(STAILQ_EMPTY(&tmp_txd->agg_list),
2300dc13fee6SSepherosa Ziehau 			    ("resursive aggregation on aggregated txdesc"));
2301dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_ONAGG),
2302dc13fee6SSepherosa Ziehau 			    ("not aggregated txdesc"));
2303dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
2304dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc uses dmamap"));
2305dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
2306dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc consumes "
2307dc13fee6SSepherosa Ziehau 			     "chimney sending buffer"));
2308dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_size == 0,
2309dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc has non-zero "
2310dc13fee6SSepherosa Ziehau 			     "chimney sending size"));
2311dc13fee6SSepherosa Ziehau 
2312dc13fee6SSepherosa Ziehau 			STAILQ_REMOVE_HEAD(&txd->agg_list, agg_link);
2313dc13fee6SSepherosa Ziehau 			tmp_txd->flags &= ~HN_TXD_FLAG_ONAGG;
2314dc13fee6SSepherosa Ziehau 			freed = hn_txdesc_put(txr, tmp_txd);
2315dc13fee6SSepherosa Ziehau 			KASSERT(freed, ("failed to free aggregated txdesc"));
2316dc13fee6SSepherosa Ziehau 		}
2317dc13fee6SSepherosa Ziehau 	}
2318dc13fee6SSepherosa Ziehau 
231915516c77SSepherosa Ziehau 	if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) {
232015516c77SSepherosa Ziehau 		KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
232115516c77SSepherosa Ziehau 		    ("chim txd uses dmamap"));
232215516c77SSepherosa Ziehau 		hn_chim_free(txr->hn_sc, txd->chim_index);
232315516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
2324dc13fee6SSepherosa Ziehau 		txd->chim_size = 0;
232515516c77SSepherosa Ziehau 	} else if (txd->flags & HN_TXD_FLAG_DMAMAP) {
232615516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag,
232715516c77SSepherosa Ziehau 		    txd->data_dmap, BUS_DMASYNC_POSTWRITE);
232815516c77SSepherosa Ziehau 		bus_dmamap_unload(txr->hn_tx_data_dtag,
232915516c77SSepherosa Ziehau 		    txd->data_dmap);
233015516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_DMAMAP;
233115516c77SSepherosa Ziehau 	}
233215516c77SSepherosa Ziehau 
233315516c77SSepherosa Ziehau 	if (txd->m != NULL) {
233415516c77SSepherosa Ziehau 		m_freem(txd->m);
233515516c77SSepherosa Ziehau 		txd->m = NULL;
233615516c77SSepherosa Ziehau 	}
233715516c77SSepherosa Ziehau 
233815516c77SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONLIST;
233915516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
234015516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
234115516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_avail >= 0 &&
234215516c77SSepherosa Ziehau 	    txr->hn_txdesc_avail < txr->hn_txdesc_cnt,
234315516c77SSepherosa Ziehau 	    ("txdesc_put: invalid txd avail %d", txr->hn_txdesc_avail));
234415516c77SSepherosa Ziehau 	txr->hn_txdesc_avail++;
234515516c77SSepherosa Ziehau 	SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
234615516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
234785e4ae1eSSepherosa Ziehau #else	/* HN_USE_TXDESC_BUFRING */
234885e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
234915516c77SSepherosa Ziehau 	atomic_add_int(&txr->hn_txdesc_avail, 1);
235015516c77SSepherosa Ziehau #endif
235185e4ae1eSSepherosa Ziehau 	buf_ring_enqueue(txr->hn_txdesc_br, txd);
235285e4ae1eSSepherosa Ziehau #endif	/* !HN_USE_TXDESC_BUFRING */
235315516c77SSepherosa Ziehau 
235415516c77SSepherosa Ziehau 	return 1;
235515516c77SSepherosa Ziehau }
235615516c77SSepherosa Ziehau 
235715516c77SSepherosa Ziehau static __inline struct hn_txdesc *
235815516c77SSepherosa Ziehau hn_txdesc_get(struct hn_tx_ring *txr)
235915516c77SSepherosa Ziehau {
236015516c77SSepherosa Ziehau 	struct hn_txdesc *txd;
236115516c77SSepherosa Ziehau 
236215516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
236315516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
236415516c77SSepherosa Ziehau 	txd = SLIST_FIRST(&txr->hn_txlist);
236515516c77SSepherosa Ziehau 	if (txd != NULL) {
236615516c77SSepherosa Ziehau 		KASSERT(txr->hn_txdesc_avail > 0,
236715516c77SSepherosa Ziehau 		    ("txdesc_get: invalid txd avail %d", txr->hn_txdesc_avail));
236815516c77SSepherosa Ziehau 		txr->hn_txdesc_avail--;
236915516c77SSepherosa Ziehau 		SLIST_REMOVE_HEAD(&txr->hn_txlist, link);
237015516c77SSepherosa Ziehau 	}
237115516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
237215516c77SSepherosa Ziehau #else
237315516c77SSepherosa Ziehau 	txd = buf_ring_dequeue_sc(txr->hn_txdesc_br);
237415516c77SSepherosa Ziehau #endif
237515516c77SSepherosa Ziehau 
237615516c77SSepherosa Ziehau 	if (txd != NULL) {
237715516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
237885e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
237915516c77SSepherosa Ziehau 		atomic_subtract_int(&txr->hn_txdesc_avail, 1);
238015516c77SSepherosa Ziehau #endif
238185e4ae1eSSepherosa Ziehau #endif	/* HN_USE_TXDESC_BUFRING */
238215516c77SSepherosa Ziehau 		KASSERT(txd->m == NULL && txd->refs == 0 &&
2383dc13fee6SSepherosa Ziehau 		    STAILQ_EMPTY(&txd->agg_list) &&
238415516c77SSepherosa Ziehau 		    txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
2385dc13fee6SSepherosa Ziehau 		    txd->chim_size == 0 &&
238615516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONLIST) &&
2387dc13fee6SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONAGG) == 0 &&
238815516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("invalid txd"));
238915516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_ONLIST;
239015516c77SSepherosa Ziehau 		txd->refs = 1;
239115516c77SSepherosa Ziehau 	}
239215516c77SSepherosa Ziehau 	return txd;
239315516c77SSepherosa Ziehau }
239415516c77SSepherosa Ziehau 
239515516c77SSepherosa Ziehau static __inline void
239615516c77SSepherosa Ziehau hn_txdesc_hold(struct hn_txdesc *txd)
239715516c77SSepherosa Ziehau {
239815516c77SSepherosa Ziehau 
239915516c77SSepherosa Ziehau 	/* 0->1 transition will never work */
240025641fc7SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
240115516c77SSepherosa Ziehau 	atomic_add_int(&txd->refs, 1);
240215516c77SSepherosa Ziehau }
240315516c77SSepherosa Ziehau 
2404dc13fee6SSepherosa Ziehau static __inline void
2405dc13fee6SSepherosa Ziehau hn_txdesc_agg(struct hn_txdesc *agg_txd, struct hn_txdesc *txd)
2406dc13fee6SSepherosa Ziehau {
2407dc13fee6SSepherosa Ziehau 
2408dc13fee6SSepherosa Ziehau 	KASSERT((agg_txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2409dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on aggregating txdesc"));
2410dc13fee6SSepherosa Ziehau 
2411dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
2412dc13fee6SSepherosa Ziehau 	    ("already aggregated"));
2413dc13fee6SSepherosa Ziehau 	KASSERT(STAILQ_EMPTY(&txd->agg_list),
2414dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on to-be-aggregated txdesc"));
2415dc13fee6SSepherosa Ziehau 
2416dc13fee6SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONAGG;
2417dc13fee6SSepherosa Ziehau 	STAILQ_INSERT_TAIL(&agg_txd->agg_list, txd, agg_link);
2418dc13fee6SSepherosa Ziehau }
2419dc13fee6SSepherosa Ziehau 
242015516c77SSepherosa Ziehau static bool
242115516c77SSepherosa Ziehau hn_tx_ring_pending(struct hn_tx_ring *txr)
242215516c77SSepherosa Ziehau {
242315516c77SSepherosa Ziehau 	bool pending = false;
242415516c77SSepherosa Ziehau 
242515516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
242615516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
242715516c77SSepherosa Ziehau 	if (txr->hn_txdesc_avail != txr->hn_txdesc_cnt)
242815516c77SSepherosa Ziehau 		pending = true;
242915516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
243015516c77SSepherosa Ziehau #else
243115516c77SSepherosa Ziehau 	if (!buf_ring_full(txr->hn_txdesc_br))
243215516c77SSepherosa Ziehau 		pending = true;
243315516c77SSepherosa Ziehau #endif
243415516c77SSepherosa Ziehau 	return (pending);
243515516c77SSepherosa Ziehau }
243615516c77SSepherosa Ziehau 
243715516c77SSepherosa Ziehau static __inline void
243815516c77SSepherosa Ziehau hn_txeof(struct hn_tx_ring *txr)
243915516c77SSepherosa Ziehau {
244015516c77SSepherosa Ziehau 	txr->hn_has_txeof = 0;
244115516c77SSepherosa Ziehau 	txr->hn_txeof(txr);
244215516c77SSepherosa Ziehau }
244315516c77SSepherosa Ziehau 
244415516c77SSepherosa Ziehau static void
244515516c77SSepherosa Ziehau hn_txpkt_done(struct hn_nvs_sendctx *sndc, struct hn_softc *sc,
244615516c77SSepherosa Ziehau     struct vmbus_channel *chan, const void *data __unused, int dlen __unused)
244715516c77SSepherosa Ziehau {
244815516c77SSepherosa Ziehau 	struct hn_txdesc *txd = sndc->hn_cbarg;
244915516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
245015516c77SSepherosa Ziehau 
245115516c77SSepherosa Ziehau 	txr = txd->txr;
245215516c77SSepherosa Ziehau 	KASSERT(txr->hn_chan == chan,
245315516c77SSepherosa Ziehau 	    ("channel mismatch, on chan%u, should be chan%u",
2454aa1a2adcSSepherosa Ziehau 	     vmbus_chan_id(chan), vmbus_chan_id(txr->hn_chan)));
245515516c77SSepherosa Ziehau 
245615516c77SSepherosa Ziehau 	txr->hn_has_txeof = 1;
245715516c77SSepherosa Ziehau 	hn_txdesc_put(txr, txd);
245815516c77SSepherosa Ziehau 
245915516c77SSepherosa Ziehau 	++txr->hn_txdone_cnt;
246015516c77SSepherosa Ziehau 	if (txr->hn_txdone_cnt >= HN_EARLY_TXEOF_THRESH) {
246115516c77SSepherosa Ziehau 		txr->hn_txdone_cnt = 0;
246215516c77SSepherosa Ziehau 		if (txr->hn_oactive)
246315516c77SSepherosa Ziehau 			hn_txeof(txr);
246415516c77SSepherosa Ziehau 	}
246515516c77SSepherosa Ziehau }
246615516c77SSepherosa Ziehau 
246715516c77SSepherosa Ziehau static void
246815516c77SSepherosa Ziehau hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
246915516c77SSepherosa Ziehau {
247015516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
247115516c77SSepherosa Ziehau 	tcp_lro_flush_all(&rxr->hn_lro);
247215516c77SSepherosa Ziehau #endif
247315516c77SSepherosa Ziehau 
247415516c77SSepherosa Ziehau 	/*
247515516c77SSepherosa Ziehau 	 * NOTE:
247615516c77SSepherosa Ziehau 	 * 'txr' could be NULL, if multiple channels and
247715516c77SSepherosa Ziehau 	 * ifnet.if_start method are enabled.
247815516c77SSepherosa Ziehau 	 */
247915516c77SSepherosa Ziehau 	if (txr == NULL || !txr->hn_has_txeof)
248015516c77SSepherosa Ziehau 		return;
248115516c77SSepherosa Ziehau 
248215516c77SSepherosa Ziehau 	txr->hn_txdone_cnt = 0;
248315516c77SSepherosa Ziehau 	hn_txeof(txr);
248415516c77SSepherosa Ziehau }
248515516c77SSepherosa Ziehau 
248615516c77SSepherosa Ziehau static __inline uint32_t
248715516c77SSepherosa Ziehau hn_rndis_pktmsg_offset(uint32_t ofs)
248815516c77SSepherosa Ziehau {
248915516c77SSepherosa Ziehau 
249015516c77SSepherosa Ziehau 	KASSERT(ofs >= sizeof(struct rndis_packet_msg),
249115516c77SSepherosa Ziehau 	    ("invalid RNDIS packet msg offset %u", ofs));
249215516c77SSepherosa Ziehau 	return (ofs - __offsetof(struct rndis_packet_msg, rm_dataoffset));
249315516c77SSepherosa Ziehau }
249415516c77SSepherosa Ziehau 
249515516c77SSepherosa Ziehau static __inline void *
249615516c77SSepherosa Ziehau hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
249715516c77SSepherosa Ziehau     size_t pi_dlen, uint32_t pi_type)
249815516c77SSepherosa Ziehau {
249915516c77SSepherosa Ziehau 	const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
250015516c77SSepherosa Ziehau 	struct rndis_pktinfo *pi;
250115516c77SSepherosa Ziehau 
250215516c77SSepherosa Ziehau 	KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
250315516c77SSepherosa Ziehau 	    ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
250415516c77SSepherosa Ziehau 
250515516c77SSepherosa Ziehau 	/*
250615516c77SSepherosa Ziehau 	 * Per-packet-info does not move; it only grows.
250715516c77SSepherosa Ziehau 	 *
250815516c77SSepherosa Ziehau 	 * NOTE:
250915516c77SSepherosa Ziehau 	 * rm_pktinfooffset in this phase counts from the beginning
251015516c77SSepherosa Ziehau 	 * of rndis_packet_msg.
251115516c77SSepherosa Ziehau 	 */
251215516c77SSepherosa Ziehau 	KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
251315516c77SSepherosa Ziehau 	    ("%u pktinfo overflows RNDIS packet msg", pi_type));
251415516c77SSepherosa Ziehau 	pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
251515516c77SSepherosa Ziehau 	    pkt->rm_pktinfolen);
251615516c77SSepherosa Ziehau 	pkt->rm_pktinfolen += pi_size;
251715516c77SSepherosa Ziehau 
251815516c77SSepherosa Ziehau 	pi->rm_size = pi_size;
251915516c77SSepherosa Ziehau 	pi->rm_type = pi_type;
252015516c77SSepherosa Ziehau 	pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
252115516c77SSepherosa Ziehau 
252215516c77SSepherosa Ziehau 	return (pi->rm_data);
252315516c77SSepherosa Ziehau }
252415516c77SSepherosa Ziehau 
2525dc13fee6SSepherosa Ziehau static __inline int
2526dc13fee6SSepherosa Ziehau hn_flush_txagg(struct ifnet *ifp, struct hn_tx_ring *txr)
2527dc13fee6SSepherosa Ziehau {
2528dc13fee6SSepherosa Ziehau 	struct hn_txdesc *txd;
2529dc13fee6SSepherosa Ziehau 	struct mbuf *m;
2530dc13fee6SSepherosa Ziehau 	int error, pkts;
2531dc13fee6SSepherosa Ziehau 
2532dc13fee6SSepherosa Ziehau 	txd = txr->hn_agg_txd;
2533dc13fee6SSepherosa Ziehau 	KASSERT(txd != NULL, ("no aggregate txdesc"));
2534dc13fee6SSepherosa Ziehau 
2535dc13fee6SSepherosa Ziehau 	/*
2536dc13fee6SSepherosa Ziehau 	 * Since hn_txpkt() will reset this temporary stat, save
2537dc13fee6SSepherosa Ziehau 	 * it now, so that oerrors can be updated properly, if
2538dc13fee6SSepherosa Ziehau 	 * hn_txpkt() ever fails.
2539dc13fee6SSepherosa Ziehau 	 */
2540dc13fee6SSepherosa Ziehau 	pkts = txr->hn_stat_pkts;
2541dc13fee6SSepherosa Ziehau 
2542dc13fee6SSepherosa Ziehau 	/*
2543dc13fee6SSepherosa Ziehau 	 * Since txd's mbuf will _not_ be freed upon hn_txpkt()
2544dc13fee6SSepherosa Ziehau 	 * failure, save it for later freeing, if hn_txpkt() ever
2545dc13fee6SSepherosa Ziehau 	 * fails.
2546dc13fee6SSepherosa Ziehau 	 */
2547dc13fee6SSepherosa Ziehau 	m = txd->m;
2548dc13fee6SSepherosa Ziehau 	error = hn_txpkt(ifp, txr, txd);
2549dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
2550dc13fee6SSepherosa Ziehau 		/* txd is freed, but m is not. */
2551dc13fee6SSepherosa Ziehau 		m_freem(m);
2552dc13fee6SSepherosa Ziehau 
2553dc13fee6SSepherosa Ziehau 		txr->hn_flush_failed++;
2554dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, pkts);
2555dc13fee6SSepherosa Ziehau 	}
2556dc13fee6SSepherosa Ziehau 
2557dc13fee6SSepherosa Ziehau 	/* Reset all aggregation states. */
2558dc13fee6SSepherosa Ziehau 	txr->hn_agg_txd = NULL;
2559dc13fee6SSepherosa Ziehau 	txr->hn_agg_szleft = 0;
2560dc13fee6SSepherosa Ziehau 	txr->hn_agg_pktleft = 0;
2561dc13fee6SSepherosa Ziehau 	txr->hn_agg_prevpkt = NULL;
2562dc13fee6SSepherosa Ziehau 
2563dc13fee6SSepherosa Ziehau 	return (error);
2564dc13fee6SSepherosa Ziehau }
2565dc13fee6SSepherosa Ziehau 
2566dc13fee6SSepherosa Ziehau static void *
2567dc13fee6SSepherosa Ziehau hn_try_txagg(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
2568dc13fee6SSepherosa Ziehau     int pktsize)
2569dc13fee6SSepherosa Ziehau {
2570dc13fee6SSepherosa Ziehau 	void *chim;
2571dc13fee6SSepherosa Ziehau 
2572dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL) {
2573dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft >= 1 && txr->hn_agg_szleft > pktsize) {
2574dc13fee6SSepherosa Ziehau 			struct hn_txdesc *agg_txd = txr->hn_agg_txd;
2575dc13fee6SSepherosa Ziehau 			struct rndis_packet_msg *pkt = txr->hn_agg_prevpkt;
2576dc13fee6SSepherosa Ziehau 			int olen;
2577dc13fee6SSepherosa Ziehau 
2578dc13fee6SSepherosa Ziehau 			/*
2579dc13fee6SSepherosa Ziehau 			 * Update the previous RNDIS packet's total length,
2580dc13fee6SSepherosa Ziehau 			 * it can be increased due to the mandatory alignment
2581dc13fee6SSepherosa Ziehau 			 * padding for this RNDIS packet.  And update the
2582dc13fee6SSepherosa Ziehau 			 * aggregating txdesc's chimney sending buffer size
2583dc13fee6SSepherosa Ziehau 			 * accordingly.
2584dc13fee6SSepherosa Ziehau 			 *
2585dc13fee6SSepherosa Ziehau 			 * XXX
2586dc13fee6SSepherosa Ziehau 			 * Zero-out the padding, as required by the RNDIS spec.
2587dc13fee6SSepherosa Ziehau 			 */
2588dc13fee6SSepherosa Ziehau 			olen = pkt->rm_len;
2589dc13fee6SSepherosa Ziehau 			pkt->rm_len = roundup2(olen, txr->hn_agg_align);
2590dc13fee6SSepherosa Ziehau 			agg_txd->chim_size += pkt->rm_len - olen;
2591dc13fee6SSepherosa Ziehau 
2592dc13fee6SSepherosa Ziehau 			/* Link this txdesc to the parent. */
2593dc13fee6SSepherosa Ziehau 			hn_txdesc_agg(agg_txd, txd);
2594dc13fee6SSepherosa Ziehau 
2595dc13fee6SSepherosa Ziehau 			chim = (uint8_t *)pkt + pkt->rm_len;
2596dc13fee6SSepherosa Ziehau 			/* Save the current packet for later fixup. */
2597dc13fee6SSepherosa Ziehau 			txr->hn_agg_prevpkt = chim;
2598dc13fee6SSepherosa Ziehau 
2599dc13fee6SSepherosa Ziehau 			txr->hn_agg_pktleft--;
2600dc13fee6SSepherosa Ziehau 			txr->hn_agg_szleft -= pktsize;
2601dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_szleft <=
2602dc13fee6SSepherosa Ziehau 			    HN_PKTSIZE_MIN(txr->hn_agg_align)) {
2603dc13fee6SSepherosa Ziehau 				/*
2604dc13fee6SSepherosa Ziehau 				 * Probably can't aggregate more packets,
2605dc13fee6SSepherosa Ziehau 				 * flush this aggregating txdesc proactively.
2606dc13fee6SSepherosa Ziehau 				 */
2607dc13fee6SSepherosa Ziehau 				txr->hn_agg_pktleft = 0;
2608dc13fee6SSepherosa Ziehau 			}
2609dc13fee6SSepherosa Ziehau 			/* Done! */
2610dc13fee6SSepherosa Ziehau 			return (chim);
2611dc13fee6SSepherosa Ziehau 		}
2612dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
2613dc13fee6SSepherosa Ziehau 	}
2614dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
2615dc13fee6SSepherosa Ziehau 
2616dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney_tried++;
2617dc13fee6SSepherosa Ziehau 	txd->chim_index = hn_chim_alloc(txr->hn_sc);
2618dc13fee6SSepherosa Ziehau 	if (txd->chim_index == HN_NVS_CHIM_IDX_INVALID)
2619dc13fee6SSepherosa Ziehau 		return (NULL);
2620dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney++;
2621dc13fee6SSepherosa Ziehau 
2622dc13fee6SSepherosa Ziehau 	chim = txr->hn_sc->hn_chim +
2623dc13fee6SSepherosa Ziehau 	    (txd->chim_index * txr->hn_sc->hn_chim_szmax);
2624dc13fee6SSepherosa Ziehau 
2625dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_pktmax > 1 &&
2626dc13fee6SSepherosa Ziehau 	    txr->hn_agg_szmax > pktsize + HN_PKTSIZE_MIN(txr->hn_agg_align)) {
2627dc13fee6SSepherosa Ziehau 		txr->hn_agg_txd = txd;
2628dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktleft = txr->hn_agg_pktmax - 1;
2629dc13fee6SSepherosa Ziehau 		txr->hn_agg_szleft = txr->hn_agg_szmax - pktsize;
2630dc13fee6SSepherosa Ziehau 		txr->hn_agg_prevpkt = chim;
2631dc13fee6SSepherosa Ziehau 	}
2632dc13fee6SSepherosa Ziehau 	return (chim);
2633dc13fee6SSepherosa Ziehau }
2634dc13fee6SSepherosa Ziehau 
263515516c77SSepherosa Ziehau /*
263615516c77SSepherosa Ziehau  * NOTE:
263715516c77SSepherosa Ziehau  * If this function fails, then both txd and m_head0 will be freed.
263815516c77SSepherosa Ziehau  */
263915516c77SSepherosa Ziehau static int
2640dc13fee6SSepherosa Ziehau hn_encap(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
2641dc13fee6SSepherosa Ziehau     struct mbuf **m_head0)
264215516c77SSepherosa Ziehau {
264315516c77SSepherosa Ziehau 	bus_dma_segment_t segs[HN_TX_DATA_SEGCNT_MAX];
264415516c77SSepherosa Ziehau 	int error, nsegs, i;
264515516c77SSepherosa Ziehau 	struct mbuf *m_head = *m_head0;
264615516c77SSepherosa Ziehau 	struct rndis_packet_msg *pkt;
264715516c77SSepherosa Ziehau 	uint32_t *pi_data;
26488966e5d5SSepherosa Ziehau 	void *chim = NULL;
2649dc13fee6SSepherosa Ziehau 	int pkt_hlen, pkt_size;
265015516c77SSepherosa Ziehau 
265115516c77SSepherosa Ziehau 	pkt = txd->rndis_pkt;
2652dc13fee6SSepherosa Ziehau 	pkt_size = HN_PKTSIZE(m_head, txr->hn_agg_align);
2653dc13fee6SSepherosa Ziehau 	if (pkt_size < txr->hn_chim_size) {
2654dc13fee6SSepherosa Ziehau 		chim = hn_try_txagg(ifp, txr, txd, pkt_size);
2655dc13fee6SSepherosa Ziehau 		if (chim != NULL)
26568966e5d5SSepherosa Ziehau 			pkt = chim;
2657dc13fee6SSepherosa Ziehau 	} else {
2658dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL)
2659dc13fee6SSepherosa Ziehau 			hn_flush_txagg(ifp, txr);
26608966e5d5SSepherosa Ziehau 	}
26618966e5d5SSepherosa Ziehau 
266215516c77SSepherosa Ziehau 	pkt->rm_type = REMOTE_NDIS_PACKET_MSG;
26638fe90f73SSepherosa Ziehau 	pkt->rm_len = m_head->m_pkthdr.len;
26649130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = 0;
266515516c77SSepherosa Ziehau 	pkt->rm_datalen = m_head->m_pkthdr.len;
2666dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataoffset = 0;
2667dc13fee6SSepherosa Ziehau 	pkt->rm_oobdatalen = 0;
2668dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataelements = 0;
266915516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = sizeof(*pkt);
267015516c77SSepherosa Ziehau 	pkt->rm_pktinfolen = 0;
2671dc13fee6SSepherosa Ziehau 	pkt->rm_vchandle = 0;
2672dc13fee6SSepherosa Ziehau 	pkt->rm_reserved = 0;
267315516c77SSepherosa Ziehau 
267415516c77SSepherosa Ziehau 	if (txr->hn_tx_flags & HN_TX_FLAG_HASHVAL) {
267515516c77SSepherosa Ziehau 		/*
267615516c77SSepherosa Ziehau 		 * Set the hash value for this packet, so that the host could
267715516c77SSepherosa Ziehau 		 * dispatch the TX done event for this packet back to this TX
267815516c77SSepherosa Ziehau 		 * ring's channel.
267915516c77SSepherosa Ziehau 		 */
268015516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
268115516c77SSepherosa Ziehau 		    HN_NDIS_HASH_VALUE_SIZE, HN_NDIS_PKTINFO_TYPE_HASHVAL);
268215516c77SSepherosa Ziehau 		*pi_data = txr->hn_tx_idx;
268315516c77SSepherosa Ziehau 	}
268415516c77SSepherosa Ziehau 
268515516c77SSepherosa Ziehau 	if (m_head->m_flags & M_VLANTAG) {
268615516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
268715516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_SIZE, NDIS_PKTINFO_TYPE_VLAN);
268815516c77SSepherosa Ziehau 		*pi_data = NDIS_VLAN_INFO_MAKE(
268915516c77SSepherosa Ziehau 		    EVL_VLANOFTAG(m_head->m_pkthdr.ether_vtag),
269015516c77SSepherosa Ziehau 		    EVL_PRIOFTAG(m_head->m_pkthdr.ether_vtag),
269115516c77SSepherosa Ziehau 		    EVL_CFIOFTAG(m_head->m_pkthdr.ether_vtag));
269215516c77SSepherosa Ziehau 	}
269315516c77SSepherosa Ziehau 
269415516c77SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
269515516c77SSepherosa Ziehau #if defined(INET6) || defined(INET)
269615516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
269715516c77SSepherosa Ziehau 		    NDIS_LSO2_INFO_SIZE, NDIS_PKTINFO_TYPE_LSO);
269815516c77SSepherosa Ziehau #ifdef INET
269915516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
270015516c77SSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV4(0,
270115516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
270215516c77SSepherosa Ziehau 		}
270315516c77SSepherosa Ziehau #endif
270415516c77SSepherosa Ziehau #if defined(INET6) && defined(INET)
270515516c77SSepherosa Ziehau 		else
270615516c77SSepherosa Ziehau #endif
270715516c77SSepherosa Ziehau #ifdef INET6
270815516c77SSepherosa Ziehau 		{
270915516c77SSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV6(0,
271015516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
271115516c77SSepherosa Ziehau 		}
271215516c77SSepherosa Ziehau #endif
271315516c77SSepherosa Ziehau #endif	/* INET6 || INET */
271415516c77SSepherosa Ziehau 	} else if (m_head->m_pkthdr.csum_flags & txr->hn_csum_assist) {
271515516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
271615516c77SSepherosa Ziehau 		    NDIS_TXCSUM_INFO_SIZE, NDIS_PKTINFO_TYPE_CSUM);
271715516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
271815516c77SSepherosa Ziehau 		    (CSUM_IP6_TCP | CSUM_IP6_UDP)) {
271915516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV6;
272015516c77SSepherosa Ziehau 		} else {
272115516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV4;
272215516c77SSepherosa Ziehau 			if (m_head->m_pkthdr.csum_flags & CSUM_IP)
272315516c77SSepherosa Ziehau 				*pi_data |= NDIS_TXCSUM_INFO_IPCS;
272415516c77SSepherosa Ziehau 		}
272515516c77SSepherosa Ziehau 
272615516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP6_TCP))
272715516c77SSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_TCPCS;
272815516c77SSepherosa Ziehau 		else if (m_head->m_pkthdr.csum_flags &
272915516c77SSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP6_UDP))
273015516c77SSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_UDPCS;
273115516c77SSepherosa Ziehau 	}
273215516c77SSepherosa Ziehau 
2733dc13fee6SSepherosa Ziehau 	pkt_hlen = pkt->rm_pktinfooffset + pkt->rm_pktinfolen;
27348fe90f73SSepherosa Ziehau 	/* Fixup RNDIS packet message total length */
27358fe90f73SSepherosa Ziehau 	pkt->rm_len += pkt_hlen;
273615516c77SSepherosa Ziehau 	/* Convert RNDIS packet message offsets */
27379130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = hn_rndis_pktmsg_offset(pkt_hlen);
273815516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = hn_rndis_pktmsg_offset(pkt->rm_pktinfooffset);
273915516c77SSepherosa Ziehau 
274015516c77SSepherosa Ziehau 	/*
27418966e5d5SSepherosa Ziehau 	 * Fast path: Chimney sending.
274215516c77SSepherosa Ziehau 	 */
27438966e5d5SSepherosa Ziehau 	if (chim != NULL) {
2744dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tgt_txd = txd;
2745dc13fee6SSepherosa Ziehau 
2746dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL) {
2747dc13fee6SSepherosa Ziehau 			tgt_txd = txr->hn_agg_txd;
2748dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
2749dc13fee6SSepherosa Ziehau 			*m_head0 = NULL;
2750dc13fee6SSepherosa Ziehau #endif
2751dc13fee6SSepherosa Ziehau 		}
2752dc13fee6SSepherosa Ziehau 
2753dc13fee6SSepherosa Ziehau 		KASSERT(pkt == chim,
2754dc13fee6SSepherosa Ziehau 		    ("RNDIS pkt not in chimney sending buffer"));
2755dc13fee6SSepherosa Ziehau 		KASSERT(tgt_txd->chim_index != HN_NVS_CHIM_IDX_INVALID,
2756dc13fee6SSepherosa Ziehau 		    ("chimney sending buffer is not used"));
2757dc13fee6SSepherosa Ziehau 		tgt_txd->chim_size += pkt->rm_len;
275815516c77SSepherosa Ziehau 
27598966e5d5SSepherosa Ziehau 		m_copydata(m_head, 0, m_head->m_pkthdr.len,
2760dc13fee6SSepherosa Ziehau 		    ((uint8_t *)chim) + pkt_hlen);
276115516c77SSepherosa Ziehau 
276215516c77SSepherosa Ziehau 		txr->hn_gpa_cnt = 0;
276315516c77SSepherosa Ziehau 		txr->hn_sendpkt = hn_txpkt_chim;
276415516c77SSepherosa Ziehau 		goto done;
276515516c77SSepherosa Ziehau 	}
2766dc13fee6SSepherosa Ziehau 
2767dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("aggregating sglist txdesc"));
27688966e5d5SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
27698966e5d5SSepherosa Ziehau 	    ("chimney buffer is used"));
27708966e5d5SSepherosa Ziehau 	KASSERT(pkt == txd->rndis_pkt, ("RNDIS pkt not in txdesc"));
277115516c77SSepherosa Ziehau 
277215516c77SSepherosa Ziehau 	error = hn_txdesc_dmamap_load(txr, txd, &m_head, segs, &nsegs);
2773dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
277415516c77SSepherosa Ziehau 		int freed;
277515516c77SSepherosa Ziehau 
277615516c77SSepherosa Ziehau 		/*
277715516c77SSepherosa Ziehau 		 * This mbuf is not linked w/ the txd yet, so free it now.
277815516c77SSepherosa Ziehau 		 */
277915516c77SSepherosa Ziehau 		m_freem(m_head);
278015516c77SSepherosa Ziehau 		*m_head0 = NULL;
278115516c77SSepherosa Ziehau 
278215516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
278315516c77SSepherosa Ziehau 		KASSERT(freed != 0,
278415516c77SSepherosa Ziehau 		    ("fail to free txd upon txdma error"));
278515516c77SSepherosa Ziehau 
278615516c77SSepherosa Ziehau 		txr->hn_txdma_failed++;
2787dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
278815516c77SSepherosa Ziehau 		return error;
278915516c77SSepherosa Ziehau 	}
279015516c77SSepherosa Ziehau 	*m_head0 = m_head;
279115516c77SSepherosa Ziehau 
279215516c77SSepherosa Ziehau 	/* +1 RNDIS packet message */
279315516c77SSepherosa Ziehau 	txr->hn_gpa_cnt = nsegs + 1;
279415516c77SSepherosa Ziehau 
279515516c77SSepherosa Ziehau 	/* send packet with page buffer */
279615516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_page = atop(txd->rndis_pkt_paddr);
279715516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_ofs = txd->rndis_pkt_paddr & PAGE_MASK;
2798dc13fee6SSepherosa Ziehau 	txr->hn_gpa[0].gpa_len = pkt_hlen;
279915516c77SSepherosa Ziehau 
280015516c77SSepherosa Ziehau 	/*
280115516c77SSepherosa Ziehau 	 * Fill the page buffers with mbuf info after the page
280215516c77SSepherosa Ziehau 	 * buffer for RNDIS packet message.
280315516c77SSepherosa Ziehau 	 */
280415516c77SSepherosa Ziehau 	for (i = 0; i < nsegs; ++i) {
280515516c77SSepherosa Ziehau 		struct vmbus_gpa *gpa = &txr->hn_gpa[i + 1];
280615516c77SSepherosa Ziehau 
280715516c77SSepherosa Ziehau 		gpa->gpa_page = atop(segs[i].ds_addr);
280815516c77SSepherosa Ziehau 		gpa->gpa_ofs = segs[i].ds_addr & PAGE_MASK;
280915516c77SSepherosa Ziehau 		gpa->gpa_len = segs[i].ds_len;
281015516c77SSepherosa Ziehau 	}
281115516c77SSepherosa Ziehau 
281215516c77SSepherosa Ziehau 	txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
281315516c77SSepherosa Ziehau 	txd->chim_size = 0;
281415516c77SSepherosa Ziehau 	txr->hn_sendpkt = hn_txpkt_sglist;
281515516c77SSepherosa Ziehau done:
281615516c77SSepherosa Ziehau 	txd->m = m_head;
281715516c77SSepherosa Ziehau 
281815516c77SSepherosa Ziehau 	/* Set the completion routine */
281915516c77SSepherosa Ziehau 	hn_nvs_sendctx_init(&txd->send_ctx, hn_txpkt_done, txd);
282015516c77SSepherosa Ziehau 
2821dc13fee6SSepherosa Ziehau 	/* Update temporary stats for later use. */
2822dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts++;
2823dc13fee6SSepherosa Ziehau 	txr->hn_stat_size += m_head->m_pkthdr.len;
2824dc13fee6SSepherosa Ziehau 	if (m_head->m_flags & M_MCAST)
2825dc13fee6SSepherosa Ziehau 		txr->hn_stat_mcasts++;
2826dc13fee6SSepherosa Ziehau 
282715516c77SSepherosa Ziehau 	return 0;
282815516c77SSepherosa Ziehau }
282915516c77SSepherosa Ziehau 
283015516c77SSepherosa Ziehau /*
283115516c77SSepherosa Ziehau  * NOTE:
283215516c77SSepherosa Ziehau  * If this function fails, then txd will be freed, but the mbuf
283315516c77SSepherosa Ziehau  * associated w/ the txd will _not_ be freed.
283415516c77SSepherosa Ziehau  */
283515516c77SSepherosa Ziehau static int
283615516c77SSepherosa Ziehau hn_txpkt(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd)
283715516c77SSepherosa Ziehau {
28388e7d3136SSepherosa Ziehau 	int error, send_failed = 0, has_bpf;
283915516c77SSepherosa Ziehau 
284015516c77SSepherosa Ziehau again:
28418e7d3136SSepherosa Ziehau 	has_bpf = bpf_peers_present(ifp->if_bpf);
28428e7d3136SSepherosa Ziehau 	if (has_bpf) {
284315516c77SSepherosa Ziehau 		/*
28448e7d3136SSepherosa Ziehau 		 * Make sure that this txd and any aggregated txds are not
28458e7d3136SSepherosa Ziehau 		 * freed before ETHER_BPF_MTAP.
284615516c77SSepherosa Ziehau 		 */
284715516c77SSepherosa Ziehau 		hn_txdesc_hold(txd);
28488e7d3136SSepherosa Ziehau 	}
284915516c77SSepherosa Ziehau 	error = txr->hn_sendpkt(txr, txd);
285015516c77SSepherosa Ziehau 	if (!error) {
28518e7d3136SSepherosa Ziehau 		if (has_bpf) {
2852dc13fee6SSepherosa Ziehau 			const struct hn_txdesc *tmp_txd;
2853dc13fee6SSepherosa Ziehau 
285415516c77SSepherosa Ziehau 			ETHER_BPF_MTAP(ifp, txd->m);
2855dc13fee6SSepherosa Ziehau 			STAILQ_FOREACH(tmp_txd, &txd->agg_list, agg_link)
2856dc13fee6SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, tmp_txd->m);
2857dc13fee6SSepherosa Ziehau 		}
2858dc13fee6SSepherosa Ziehau 
2859dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, txr->hn_stat_pkts);
286023bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
286123bf9e15SSepherosa Ziehau 		if (!hn_use_if_start)
286223bf9e15SSepherosa Ziehau #endif
286323bf9e15SSepherosa Ziehau 		{
286415516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OBYTES,
2865dc13fee6SSepherosa Ziehau 			    txr->hn_stat_size);
2866dc13fee6SSepherosa Ziehau 			if (txr->hn_stat_mcasts != 0) {
2867dc13fee6SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OMCASTS,
2868dc13fee6SSepherosa Ziehau 				    txr->hn_stat_mcasts);
286915516c77SSepherosa Ziehau 			}
2870dc13fee6SSepherosa Ziehau 		}
2871dc13fee6SSepherosa Ziehau 		txr->hn_pkts += txr->hn_stat_pkts;
2872dc13fee6SSepherosa Ziehau 		txr->hn_sends++;
287315516c77SSepherosa Ziehau 	}
28748e7d3136SSepherosa Ziehau 	if (has_bpf)
287515516c77SSepherosa Ziehau 		hn_txdesc_put(txr, txd);
287615516c77SSepherosa Ziehau 
287715516c77SSepherosa Ziehau 	if (__predict_false(error)) {
287815516c77SSepherosa Ziehau 		int freed;
287915516c77SSepherosa Ziehau 
288015516c77SSepherosa Ziehau 		/*
288115516c77SSepherosa Ziehau 		 * This should "really rarely" happen.
288215516c77SSepherosa Ziehau 		 *
288315516c77SSepherosa Ziehau 		 * XXX Too many RX to be acked or too many sideband
288415516c77SSepherosa Ziehau 		 * commands to run?  Ask netvsc_channel_rollup()
288515516c77SSepherosa Ziehau 		 * to kick start later.
288615516c77SSepherosa Ziehau 		 */
288715516c77SSepherosa Ziehau 		txr->hn_has_txeof = 1;
288815516c77SSepherosa Ziehau 		if (!send_failed) {
288915516c77SSepherosa Ziehau 			txr->hn_send_failed++;
289015516c77SSepherosa Ziehau 			send_failed = 1;
289115516c77SSepherosa Ziehau 			/*
289215516c77SSepherosa Ziehau 			 * Try sending again after set hn_has_txeof;
289315516c77SSepherosa Ziehau 			 * in case that we missed the last
289415516c77SSepherosa Ziehau 			 * netvsc_channel_rollup().
289515516c77SSepherosa Ziehau 			 */
289615516c77SSepherosa Ziehau 			goto again;
289715516c77SSepherosa Ziehau 		}
289815516c77SSepherosa Ziehau 		if_printf(ifp, "send failed\n");
289915516c77SSepherosa Ziehau 
290015516c77SSepherosa Ziehau 		/*
290115516c77SSepherosa Ziehau 		 * Caller will perform further processing on the
290215516c77SSepherosa Ziehau 		 * associated mbuf, so don't free it in hn_txdesc_put();
290315516c77SSepherosa Ziehau 		 * only unload it from the DMA map in hn_txdesc_put(),
290415516c77SSepherosa Ziehau 		 * if it was loaded.
290515516c77SSepherosa Ziehau 		 */
290615516c77SSepherosa Ziehau 		txd->m = NULL;
290715516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
290815516c77SSepherosa Ziehau 		KASSERT(freed != 0,
290915516c77SSepherosa Ziehau 		    ("fail to free txd upon send error"));
291015516c77SSepherosa Ziehau 
291115516c77SSepherosa Ziehau 		txr->hn_send_failed++;
291215516c77SSepherosa Ziehau 	}
2913dc13fee6SSepherosa Ziehau 
2914dc13fee6SSepherosa Ziehau 	/* Reset temporary stats, after this sending is done. */
2915dc13fee6SSepherosa Ziehau 	txr->hn_stat_size = 0;
2916dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts = 0;
2917dc13fee6SSepherosa Ziehau 	txr->hn_stat_mcasts = 0;
2918dc13fee6SSepherosa Ziehau 
2919dc13fee6SSepherosa Ziehau 	return (error);
292015516c77SSepherosa Ziehau }
292115516c77SSepherosa Ziehau 
292215516c77SSepherosa Ziehau /*
292315516c77SSepherosa Ziehau  * Append the specified data to the indicated mbuf chain,
292415516c77SSepherosa Ziehau  * Extend the mbuf chain if the new data does not fit in
292515516c77SSepherosa Ziehau  * existing space.
292615516c77SSepherosa Ziehau  *
292715516c77SSepherosa Ziehau  * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
292815516c77SSepherosa Ziehau  * There should be an equivalent in the kernel mbuf code,
292915516c77SSepherosa Ziehau  * but there does not appear to be one yet.
293015516c77SSepherosa Ziehau  *
293115516c77SSepherosa Ziehau  * Differs from m_append() in that additional mbufs are
293215516c77SSepherosa Ziehau  * allocated with cluster size MJUMPAGESIZE, and filled
293315516c77SSepherosa Ziehau  * accordingly.
293415516c77SSepherosa Ziehau  *
293515516c77SSepherosa Ziehau  * Return 1 if able to complete the job; otherwise 0.
293615516c77SSepherosa Ziehau  */
293715516c77SSepherosa Ziehau static int
293815516c77SSepherosa Ziehau hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
293915516c77SSepherosa Ziehau {
294015516c77SSepherosa Ziehau 	struct mbuf *m, *n;
294115516c77SSepherosa Ziehau 	int remainder, space;
294215516c77SSepherosa Ziehau 
294315516c77SSepherosa Ziehau 	for (m = m0; m->m_next != NULL; m = m->m_next)
294415516c77SSepherosa Ziehau 		;
294515516c77SSepherosa Ziehau 	remainder = len;
294615516c77SSepherosa Ziehau 	space = M_TRAILINGSPACE(m);
294715516c77SSepherosa Ziehau 	if (space > 0) {
294815516c77SSepherosa Ziehau 		/*
294915516c77SSepherosa Ziehau 		 * Copy into available space.
295015516c77SSepherosa Ziehau 		 */
295115516c77SSepherosa Ziehau 		if (space > remainder)
295215516c77SSepherosa Ziehau 			space = remainder;
295315516c77SSepherosa Ziehau 		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
295415516c77SSepherosa Ziehau 		m->m_len += space;
295515516c77SSepherosa Ziehau 		cp += space;
295615516c77SSepherosa Ziehau 		remainder -= space;
295715516c77SSepherosa Ziehau 	}
295815516c77SSepherosa Ziehau 	while (remainder > 0) {
295915516c77SSepherosa Ziehau 		/*
296015516c77SSepherosa Ziehau 		 * Allocate a new mbuf; could check space
296115516c77SSepherosa Ziehau 		 * and allocate a cluster instead.
296215516c77SSepherosa Ziehau 		 */
296315516c77SSepherosa Ziehau 		n = m_getjcl(M_NOWAIT, m->m_type, 0, MJUMPAGESIZE);
296415516c77SSepherosa Ziehau 		if (n == NULL)
296515516c77SSepherosa Ziehau 			break;
296615516c77SSepherosa Ziehau 		n->m_len = min(MJUMPAGESIZE, remainder);
296715516c77SSepherosa Ziehau 		bcopy(cp, mtod(n, caddr_t), n->m_len);
296815516c77SSepherosa Ziehau 		cp += n->m_len;
296915516c77SSepherosa Ziehau 		remainder -= n->m_len;
297015516c77SSepherosa Ziehau 		m->m_next = n;
297115516c77SSepherosa Ziehau 		m = n;
297215516c77SSepherosa Ziehau 	}
297315516c77SSepherosa Ziehau 	if (m0->m_flags & M_PKTHDR)
297415516c77SSepherosa Ziehau 		m0->m_pkthdr.len += len - remainder;
297515516c77SSepherosa Ziehau 
297615516c77SSepherosa Ziehau 	return (remainder == 0);
297715516c77SSepherosa Ziehau }
297815516c77SSepherosa Ziehau 
297915516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
298015516c77SSepherosa Ziehau static __inline int
298115516c77SSepherosa Ziehau hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m)
298215516c77SSepherosa Ziehau {
298315516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
298415516c77SSepherosa Ziehau 	if (hn_lro_mbufq_depth) {
298515516c77SSepherosa Ziehau 		tcp_lro_queue_mbuf(lc, m);
298615516c77SSepherosa Ziehau 		return 0;
298715516c77SSepherosa Ziehau 	}
298815516c77SSepherosa Ziehau #endif
298915516c77SSepherosa Ziehau 	return tcp_lro_rx(lc, m, 0);
299015516c77SSepherosa Ziehau }
299115516c77SSepherosa Ziehau #endif
299215516c77SSepherosa Ziehau 
299315516c77SSepherosa Ziehau static int
299415516c77SSepherosa Ziehau hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
299515516c77SSepherosa Ziehau     const struct hn_rxinfo *info)
299615516c77SSepherosa Ziehau {
29975bdfd3fdSDexuan Cui 	struct ifnet *ifp;
299815516c77SSepherosa Ziehau 	struct mbuf *m_new;
299915516c77SSepherosa Ziehau 	int size, do_lro = 0, do_csum = 1;
300015516c77SSepherosa Ziehau 	int hash_type;
300115516c77SSepherosa Ziehau 
30025bdfd3fdSDexuan Cui 	/* If the VF is active, inject the packet through the VF */
3003499c3e17SSepherosa Ziehau 	ifp = rxr->hn_rxvf_ifp ? rxr->hn_rxvf_ifp : rxr->hn_ifp;
30045bdfd3fdSDexuan Cui 
3005b3b75d9cSSepherosa Ziehau 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
3006b3b75d9cSSepherosa Ziehau 		/*
3007b3b75d9cSSepherosa Ziehau 		 * NOTE:
3008b3b75d9cSSepherosa Ziehau 		 * See the NOTE of hn_rndis_init_fixat().  This
3009b3b75d9cSSepherosa Ziehau 		 * function can be reached, immediately after the
3010b3b75d9cSSepherosa Ziehau 		 * RNDIS is initialized but before the ifnet is
3011b3b75d9cSSepherosa Ziehau 		 * setup on the hn_attach() path; drop the unexpected
3012b3b75d9cSSepherosa Ziehau 		 * packets.
3013b3b75d9cSSepherosa Ziehau 		 */
3014b3b75d9cSSepherosa Ziehau 		return (0);
3015b3b75d9cSSepherosa Ziehau 	}
3016b3b75d9cSSepherosa Ziehau 
3017c927d681SDexuan Cui 	if (dlen <= MHLEN) {
301815516c77SSepherosa Ziehau 		m_new = m_gethdr(M_NOWAIT, MT_DATA);
301915516c77SSepherosa Ziehau 		if (m_new == NULL) {
302015516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
302115516c77SSepherosa Ziehau 			return (0);
302215516c77SSepherosa Ziehau 		}
302315516c77SSepherosa Ziehau 		memcpy(mtod(m_new, void *), data, dlen);
302415516c77SSepherosa Ziehau 		m_new->m_pkthdr.len = m_new->m_len = dlen;
302515516c77SSepherosa Ziehau 		rxr->hn_small_pkts++;
302615516c77SSepherosa Ziehau 	} else {
302715516c77SSepherosa Ziehau 		/*
302815516c77SSepherosa Ziehau 		 * Get an mbuf with a cluster.  For packets 2K or less,
302915516c77SSepherosa Ziehau 		 * get a standard 2K cluster.  For anything larger, get a
303015516c77SSepherosa Ziehau 		 * 4K cluster.  Any buffers larger than 4K can cause problems
303115516c77SSepherosa Ziehau 		 * if looped around to the Hyper-V TX channel, so avoid them.
303215516c77SSepherosa Ziehau 		 */
303315516c77SSepherosa Ziehau 		size = MCLBYTES;
303415516c77SSepherosa Ziehau 		if (dlen > MCLBYTES) {
303515516c77SSepherosa Ziehau 			/* 4096 */
303615516c77SSepherosa Ziehau 			size = MJUMPAGESIZE;
303715516c77SSepherosa Ziehau 		}
303815516c77SSepherosa Ziehau 
303915516c77SSepherosa Ziehau 		m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size);
304015516c77SSepherosa Ziehau 		if (m_new == NULL) {
304115516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
304215516c77SSepherosa Ziehau 			return (0);
304315516c77SSepherosa Ziehau 		}
304415516c77SSepherosa Ziehau 
304515516c77SSepherosa Ziehau 		hv_m_append(m_new, dlen, data);
304615516c77SSepherosa Ziehau 	}
304715516c77SSepherosa Ziehau 	m_new->m_pkthdr.rcvif = ifp;
304815516c77SSepherosa Ziehau 
304915516c77SSepherosa Ziehau 	if (__predict_false((ifp->if_capenable & IFCAP_RXCSUM) == 0))
305015516c77SSepherosa Ziehau 		do_csum = 0;
305115516c77SSepherosa Ziehau 
305215516c77SSepherosa Ziehau 	/* receive side checksum offload */
305315516c77SSepherosa Ziehau 	if (info->csum_info != HN_NDIS_RXCSUM_INFO_INVALID) {
305415516c77SSepherosa Ziehau 		/* IP csum offload */
305515516c77SSepherosa Ziehau 		if ((info->csum_info & NDIS_RXCSUM_INFO_IPCS_OK) && do_csum) {
305615516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
305715516c77SSepherosa Ziehau 			    (CSUM_IP_CHECKED | CSUM_IP_VALID);
305815516c77SSepherosa Ziehau 			rxr->hn_csum_ip++;
305915516c77SSepherosa Ziehau 		}
306015516c77SSepherosa Ziehau 
306115516c77SSepherosa Ziehau 		/* TCP/UDP csum offload */
306215516c77SSepherosa Ziehau 		if ((info->csum_info & (NDIS_RXCSUM_INFO_UDPCS_OK |
306315516c77SSepherosa Ziehau 		     NDIS_RXCSUM_INFO_TCPCS_OK)) && do_csum) {
306415516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
306515516c77SSepherosa Ziehau 			    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
306615516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_data = 0xffff;
306715516c77SSepherosa Ziehau 			if (info->csum_info & NDIS_RXCSUM_INFO_TCPCS_OK)
306815516c77SSepherosa Ziehau 				rxr->hn_csum_tcp++;
306915516c77SSepherosa Ziehau 			else
307015516c77SSepherosa Ziehau 				rxr->hn_csum_udp++;
307115516c77SSepherosa Ziehau 		}
307215516c77SSepherosa Ziehau 
307315516c77SSepherosa Ziehau 		/*
307415516c77SSepherosa Ziehau 		 * XXX
307515516c77SSepherosa Ziehau 		 * As of this write (Oct 28th, 2016), host side will turn
307615516c77SSepherosa Ziehau 		 * on only TCPCS_OK and IPCS_OK even for UDP datagrams, so
307715516c77SSepherosa Ziehau 		 * the do_lro setting here is actually _not_ accurate.  We
307815516c77SSepherosa Ziehau 		 * depend on the RSS hash type check to reset do_lro.
307915516c77SSepherosa Ziehau 		 */
308015516c77SSepherosa Ziehau 		if ((info->csum_info &
308115516c77SSepherosa Ziehau 		     (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) ==
308215516c77SSepherosa Ziehau 		    (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK))
308315516c77SSepherosa Ziehau 			do_lro = 1;
308415516c77SSepherosa Ziehau 	} else {
308515516c77SSepherosa Ziehau 		const struct ether_header *eh;
308615516c77SSepherosa Ziehau 		uint16_t etype;
308715516c77SSepherosa Ziehau 		int hoff;
308815516c77SSepherosa Ziehau 
308915516c77SSepherosa Ziehau 		hoff = sizeof(*eh);
309015516c77SSepherosa Ziehau 		if (m_new->m_len < hoff)
309115516c77SSepherosa Ziehau 			goto skip;
309215516c77SSepherosa Ziehau 		eh = mtod(m_new, struct ether_header *);
309315516c77SSepherosa Ziehau 		etype = ntohs(eh->ether_type);
309415516c77SSepherosa Ziehau 		if (etype == ETHERTYPE_VLAN) {
309515516c77SSepherosa Ziehau 			const struct ether_vlan_header *evl;
309615516c77SSepherosa Ziehau 
309715516c77SSepherosa Ziehau 			hoff = sizeof(*evl);
309815516c77SSepherosa Ziehau 			if (m_new->m_len < hoff)
309915516c77SSepherosa Ziehau 				goto skip;
310015516c77SSepherosa Ziehau 			evl = mtod(m_new, struct ether_vlan_header *);
310115516c77SSepherosa Ziehau 			etype = ntohs(evl->evl_proto);
310215516c77SSepherosa Ziehau 		}
310315516c77SSepherosa Ziehau 
310415516c77SSepherosa Ziehau 		if (etype == ETHERTYPE_IP) {
310515516c77SSepherosa Ziehau 			int pr;
310615516c77SSepherosa Ziehau 
310715516c77SSepherosa Ziehau 			pr = hn_check_iplen(m_new, hoff);
310815516c77SSepherosa Ziehau 			if (pr == IPPROTO_TCP) {
310915516c77SSepherosa Ziehau 				if (do_csum &&
311015516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
311115516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_TCP)) {
311215516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
311315516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
311415516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
311515516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
311615516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
311715516c77SSepherosa Ziehau 				}
311815516c77SSepherosa Ziehau 				do_lro = 1;
311915516c77SSepherosa Ziehau 			} else if (pr == IPPROTO_UDP) {
312015516c77SSepherosa Ziehau 				if (do_csum &&
312115516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
312215516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_UDP)) {
312315516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
312415516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
312515516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
312615516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
312715516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
312815516c77SSepherosa Ziehau 				}
312915516c77SSepherosa Ziehau 			} else if (pr != IPPROTO_DONE && do_csum &&
313015516c77SSepherosa Ziehau 			    (rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) {
313115516c77SSepherosa Ziehau 				rxr->hn_csum_trusted++;
313215516c77SSepherosa Ziehau 				m_new->m_pkthdr.csum_flags |=
313315516c77SSepherosa Ziehau 				    (CSUM_IP_CHECKED | CSUM_IP_VALID);
313415516c77SSepherosa Ziehau 			}
313515516c77SSepherosa Ziehau 		}
313615516c77SSepherosa Ziehau 	}
313715516c77SSepherosa Ziehau skip:
313815516c77SSepherosa Ziehau 	if (info->vlan_info != HN_NDIS_VLAN_INFO_INVALID) {
313915516c77SSepherosa Ziehau 		m_new->m_pkthdr.ether_vtag = EVL_MAKETAG(
314015516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_ID(info->vlan_info),
314115516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_PRI(info->vlan_info),
314215516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_CFI(info->vlan_info));
314315516c77SSepherosa Ziehau 		m_new->m_flags |= M_VLANTAG;
314415516c77SSepherosa Ziehau 	}
314515516c77SSepherosa Ziehau 
314615516c77SSepherosa Ziehau 	if (info->hash_info != HN_NDIS_HASH_INFO_INVALID) {
314715516c77SSepherosa Ziehau 		rxr->hn_rss_pkts++;
314815516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = info->hash_value;
314915516c77SSepherosa Ziehau 		hash_type = M_HASHTYPE_OPAQUE_HASH;
315015516c77SSepherosa Ziehau 		if ((info->hash_info & NDIS_HASH_FUNCTION_MASK) ==
315115516c77SSepherosa Ziehau 		    NDIS_HASH_FUNCTION_TOEPLITZ) {
315215516c77SSepherosa Ziehau 			uint32_t type = (info->hash_info & NDIS_HASH_TYPE_MASK);
315315516c77SSepherosa Ziehau 
315415516c77SSepherosa Ziehau 			/*
315515516c77SSepherosa Ziehau 			 * NOTE:
315615516c77SSepherosa Ziehau 			 * do_lro is resetted, if the hash types are not TCP
315715516c77SSepherosa Ziehau 			 * related.  See the comment in the above csum_flags
315815516c77SSepherosa Ziehau 			 * setup section.
315915516c77SSepherosa Ziehau 			 */
316015516c77SSepherosa Ziehau 			switch (type) {
316115516c77SSepherosa Ziehau 			case NDIS_HASH_IPV4:
316215516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV4;
316315516c77SSepherosa Ziehau 				do_lro = 0;
316415516c77SSepherosa Ziehau 				break;
316515516c77SSepherosa Ziehau 
316615516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV4:
316715516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV4;
316815516c77SSepherosa Ziehau 				break;
316915516c77SSepherosa Ziehau 
317015516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6:
317115516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6;
317215516c77SSepherosa Ziehau 				do_lro = 0;
317315516c77SSepherosa Ziehau 				break;
317415516c77SSepherosa Ziehau 
317515516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6_EX:
317615516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6_EX;
317715516c77SSepherosa Ziehau 				do_lro = 0;
317815516c77SSepherosa Ziehau 				break;
317915516c77SSepherosa Ziehau 
318015516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6:
318115516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6;
318215516c77SSepherosa Ziehau 				break;
318315516c77SSepherosa Ziehau 
318415516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6_EX:
318515516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6_EX;
318615516c77SSepherosa Ziehau 				break;
318715516c77SSepherosa Ziehau 			}
318815516c77SSepherosa Ziehau 		}
318915516c77SSepherosa Ziehau 	} else {
319015516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = rxr->hn_rx_idx;
319115516c77SSepherosa Ziehau 		hash_type = M_HASHTYPE_OPAQUE;
319215516c77SSepherosa Ziehau 	}
319315516c77SSepherosa Ziehau 	M_HASHTYPE_SET(m_new, hash_type);
319415516c77SSepherosa Ziehau 
319515516c77SSepherosa Ziehau 	/*
319615516c77SSepherosa Ziehau 	 * Note:  Moved RX completion back to hv_nv_on_receive() so all
319715516c77SSepherosa Ziehau 	 * messages (not just data messages) will trigger a response.
319815516c77SSepherosa Ziehau 	 */
319915516c77SSepherosa Ziehau 
320015516c77SSepherosa Ziehau 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
320115516c77SSepherosa Ziehau 	rxr->hn_pkts++;
320215516c77SSepherosa Ziehau 
320315516c77SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_LRO) && do_lro) {
320415516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
320515516c77SSepherosa Ziehau 		struct lro_ctrl *lro = &rxr->hn_lro;
320615516c77SSepherosa Ziehau 
320715516c77SSepherosa Ziehau 		if (lro->lro_cnt) {
320815516c77SSepherosa Ziehau 			rxr->hn_lro_tried++;
320915516c77SSepherosa Ziehau 			if (hn_lro_rx(lro, m_new) == 0) {
321015516c77SSepherosa Ziehau 				/* DONE! */
321115516c77SSepherosa Ziehau 				return 0;
321215516c77SSepherosa Ziehau 			}
321315516c77SSepherosa Ziehau 		}
321415516c77SSepherosa Ziehau #endif
321515516c77SSepherosa Ziehau 	}
321615516c77SSepherosa Ziehau 
321715516c77SSepherosa Ziehau 	/* We're not holding the lock here, so don't release it */
321815516c77SSepherosa Ziehau 	(*ifp->if_input)(ifp, m_new);
321915516c77SSepherosa Ziehau 
322015516c77SSepherosa Ziehau 	return (0);
322115516c77SSepherosa Ziehau }
322215516c77SSepherosa Ziehau 
322315516c77SSepherosa Ziehau static int
322415516c77SSepherosa Ziehau hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
322515516c77SSepherosa Ziehau {
322615516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
32279c6cae24SSepherosa Ziehau 	struct ifreq *ifr = (struct ifreq *)data, ifr_vf;
32289c6cae24SSepherosa Ziehau 	struct ifnet *vf_ifp;
322915516c77SSepherosa Ziehau 	int mask, error = 0;
323015516c77SSepherosa Ziehau 
323115516c77SSepherosa Ziehau 	switch (cmd) {
323215516c77SSepherosa Ziehau 	case SIOCSIFMTU:
323315516c77SSepherosa Ziehau 		if (ifr->ifr_mtu > HN_MTU_MAX) {
323415516c77SSepherosa Ziehau 			error = EINVAL;
323515516c77SSepherosa Ziehau 			break;
323615516c77SSepherosa Ziehau 		}
323715516c77SSepherosa Ziehau 
323815516c77SSepherosa Ziehau 		HN_LOCK(sc);
323915516c77SSepherosa Ziehau 
324015516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
324115516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
324215516c77SSepherosa Ziehau 			break;
324315516c77SSepherosa Ziehau 		}
324415516c77SSepherosa Ziehau 
324515516c77SSepherosa Ziehau 		if ((sc->hn_caps & HN_CAP_MTU) == 0) {
324615516c77SSepherosa Ziehau 			/* Can't change MTU */
324715516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
324815516c77SSepherosa Ziehau 			error = EOPNOTSUPP;
324915516c77SSepherosa Ziehau 			break;
325015516c77SSepherosa Ziehau 		}
325115516c77SSepherosa Ziehau 
325215516c77SSepherosa Ziehau 		if (ifp->if_mtu == ifr->ifr_mtu) {
325315516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
325415516c77SSepherosa Ziehau 			break;
325515516c77SSepherosa Ziehau 		}
325615516c77SSepherosa Ziehau 
32579c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
32589c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
32599c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
32609c6cae24SSepherosa Ziehau 			strlcpy(ifr_vf.ifr_name, vf_ifp->if_xname,
32619c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
32629c6cae24SSepherosa Ziehau 			error = vf_ifp->if_ioctl(vf_ifp, SIOCSIFMTU,
32639c6cae24SSepherosa Ziehau 			    (caddr_t)&ifr_vf);
32649c6cae24SSepherosa Ziehau 			if (error) {
32659c6cae24SSepherosa Ziehau 				HN_UNLOCK(sc);
32669c6cae24SSepherosa Ziehau 				if_printf(ifp, "%s SIOCSIFMTU %d failed: %d\n",
32679c6cae24SSepherosa Ziehau 				    vf_ifp->if_xname, ifr->ifr_mtu, error);
32689c6cae24SSepherosa Ziehau 				break;
32699c6cae24SSepherosa Ziehau 			}
32709c6cae24SSepherosa Ziehau 		}
32719c6cae24SSepherosa Ziehau 
327215516c77SSepherosa Ziehau 		/*
327315516c77SSepherosa Ziehau 		 * Suspend this interface before the synthetic parts
327415516c77SSepherosa Ziehau 		 * are ripped.
327515516c77SSepherosa Ziehau 		 */
327615516c77SSepherosa Ziehau 		hn_suspend(sc);
327715516c77SSepherosa Ziehau 
327815516c77SSepherosa Ziehau 		/*
327915516c77SSepherosa Ziehau 		 * Detach the synthetics parts, i.e. NVS and RNDIS.
328015516c77SSepherosa Ziehau 		 */
328115516c77SSepherosa Ziehau 		hn_synth_detach(sc);
328215516c77SSepherosa Ziehau 
328315516c77SSepherosa Ziehau 		/*
328415516c77SSepherosa Ziehau 		 * Reattach the synthetic parts, i.e. NVS and RNDIS,
328515516c77SSepherosa Ziehau 		 * with the new MTU setting.
328615516c77SSepherosa Ziehau 		 */
328715516c77SSepherosa Ziehau 		error = hn_synth_attach(sc, ifr->ifr_mtu);
328815516c77SSepherosa Ziehau 		if (error) {
328915516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
329015516c77SSepherosa Ziehau 			break;
329115516c77SSepherosa Ziehau 		}
329215516c77SSepherosa Ziehau 
329315516c77SSepherosa Ziehau 		/*
329415516c77SSepherosa Ziehau 		 * Commit the requested MTU, after the synthetic parts
329515516c77SSepherosa Ziehau 		 * have been successfully attached.
329615516c77SSepherosa Ziehau 		 */
329715516c77SSepherosa Ziehau 		ifp->if_mtu = ifr->ifr_mtu;
329815516c77SSepherosa Ziehau 
329915516c77SSepherosa Ziehau 		/*
33009c6cae24SSepherosa Ziehau 		 * Synthetic parts' reattach may change the chimney
33019c6cae24SSepherosa Ziehau 		 * sending size; update it.
330215516c77SSepherosa Ziehau 		 */
330315516c77SSepherosa Ziehau 		if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
330415516c77SSepherosa Ziehau 			hn_set_chim_size(sc, sc->hn_chim_szmax);
33059c6cae24SSepherosa Ziehau 
33069c6cae24SSepherosa Ziehau 		/*
33079c6cae24SSepherosa Ziehau 		 * Make sure that various parameters based on MTU are
33089c6cae24SSepherosa Ziehau 		 * still valid, after the MTU change.
33099c6cae24SSepherosa Ziehau 		 */
33109c6cae24SSepherosa Ziehau 		hn_mtu_change_fixup(sc);
331115516c77SSepherosa Ziehau 
331215516c77SSepherosa Ziehau 		/*
331315516c77SSepherosa Ziehau 		 * All done!  Resume the interface now.
331415516c77SSepherosa Ziehau 		 */
331515516c77SSepherosa Ziehau 		hn_resume(sc);
331615516c77SSepherosa Ziehau 
33179c6cae24SSepherosa Ziehau 		if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
33189c6cae24SSepherosa Ziehau 			/*
33199c6cae24SSepherosa Ziehau 			 * Since we have reattached the NVS part,
33209c6cae24SSepherosa Ziehau 			 * change the datapath to VF again; in case
33219c6cae24SSepherosa Ziehau 			 * that it is lost, after the NVS was detached.
33229c6cae24SSepherosa Ziehau 			 */
33239c6cae24SSepherosa Ziehau 			hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_VF);
33249c6cae24SSepherosa Ziehau 		}
33259c6cae24SSepherosa Ziehau 
332615516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
332715516c77SSepherosa Ziehau 		break;
332815516c77SSepherosa Ziehau 
332915516c77SSepherosa Ziehau 	case SIOCSIFFLAGS:
333015516c77SSepherosa Ziehau 		HN_LOCK(sc);
333115516c77SSepherosa Ziehau 
333215516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
333315516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
333415516c77SSepherosa Ziehau 			break;
333515516c77SSepherosa Ziehau 		}
333615516c77SSepherosa Ziehau 
33379c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc))
33389c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
33399c6cae24SSepherosa Ziehau 
334015516c77SSepherosa Ziehau 		if (ifp->if_flags & IFF_UP) {
3341fdc4f478SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3342fdc4f478SSepherosa Ziehau 				/*
3343fdc4f478SSepherosa Ziehau 				 * Caller meight hold mutex, e.g.
3344fdc4f478SSepherosa Ziehau 				 * bpf; use busy-wait for the RNDIS
3345fdc4f478SSepherosa Ziehau 				 * reply.
3346fdc4f478SSepherosa Ziehau 				 */
3347fdc4f478SSepherosa Ziehau 				HN_NO_SLEEPING(sc);
3348c08f7b2cSSepherosa Ziehau 				hn_rxfilter_config(sc);
3349fdc4f478SSepherosa Ziehau 				HN_SLEEPING_OK(sc);
33509c6cae24SSepherosa Ziehau 
33519c6cae24SSepherosa Ziehau 				if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
33529c6cae24SSepherosa Ziehau 					error = hn_xpnt_vf_iocsetflags(sc);
3353fdc4f478SSepherosa Ziehau 			} else {
335415516c77SSepherosa Ziehau 				hn_init_locked(sc);
3355fdc4f478SSepherosa Ziehau 			}
335615516c77SSepherosa Ziehau 		} else {
335715516c77SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
33585bdfd3fdSDexuan Cui 				hn_stop(sc, false);
335915516c77SSepherosa Ziehau 		}
336015516c77SSepherosa Ziehau 		sc->hn_if_flags = ifp->if_flags;
336115516c77SSepherosa Ziehau 
336215516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
336315516c77SSepherosa Ziehau 		break;
336415516c77SSepherosa Ziehau 
336515516c77SSepherosa Ziehau 	case SIOCSIFCAP:
336615516c77SSepherosa Ziehau 		HN_LOCK(sc);
33679c6cae24SSepherosa Ziehau 
33689c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
33699c6cae24SSepherosa Ziehau 			ifr_vf = *ifr;
33709c6cae24SSepherosa Ziehau 			strlcpy(ifr_vf.ifr_name, sc->hn_vf_ifp->if_xname,
33719c6cae24SSepherosa Ziehau 			    sizeof(ifr_vf.ifr_name));
33729c6cae24SSepherosa Ziehau 			error = hn_xpnt_vf_iocsetcaps(sc, &ifr_vf);
33739c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
33749c6cae24SSepherosa Ziehau 			break;
33759c6cae24SSepherosa Ziehau 		}
33769c6cae24SSepherosa Ziehau 
33779c6cae24SSepherosa Ziehau 		/*
33789c6cae24SSepherosa Ziehau 		 * Fix up requested capabilities w/ supported capabilities,
33799c6cae24SSepherosa Ziehau 		 * since the supported capabilities could have been changed.
33809c6cae24SSepherosa Ziehau 		 */
33819c6cae24SSepherosa Ziehau 		mask = (ifr->ifr_reqcap & ifp->if_capabilities) ^
33829c6cae24SSepherosa Ziehau 		    ifp->if_capenable;
338315516c77SSepherosa Ziehau 
338415516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM) {
338515516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
338615516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
338715516c77SSepherosa Ziehau 				ifp->if_hwassist |= HN_CSUM_IP_HWASSIST(sc);
338815516c77SSepherosa Ziehau 			else
338915516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~HN_CSUM_IP_HWASSIST(sc);
339015516c77SSepherosa Ziehau 		}
339115516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM_IPV6) {
339215516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM_IPV6;
339315516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
339415516c77SSepherosa Ziehau 				ifp->if_hwassist |= HN_CSUM_IP6_HWASSIST(sc);
339515516c77SSepherosa Ziehau 			else
339615516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~HN_CSUM_IP6_HWASSIST(sc);
339715516c77SSepherosa Ziehau 		}
339815516c77SSepherosa Ziehau 
339915516c77SSepherosa Ziehau 		/* TODO: flip RNDIS offload parameters for RXCSUM. */
340015516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
340115516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
340215516c77SSepherosa Ziehau #ifdef foo
340315516c77SSepherosa Ziehau 		/* We can't diff IPv6 packets from IPv4 packets on RX path. */
340415516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM_IPV6)
340515516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM_IPV6;
340615516c77SSepherosa Ziehau #endif
340715516c77SSepherosa Ziehau 
340815516c77SSepherosa Ziehau 		if (mask & IFCAP_LRO)
340915516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_LRO;
341015516c77SSepherosa Ziehau 
341115516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO4) {
341215516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO4;
341315516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO4)
341415516c77SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_IP_TSO;
341515516c77SSepherosa Ziehau 			else
341615516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_IP_TSO;
341715516c77SSepherosa Ziehau 		}
341815516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO6) {
341915516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO6;
342015516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO6)
342115516c77SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_IP6_TSO;
342215516c77SSepherosa Ziehau 			else
342315516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_IP6_TSO;
342415516c77SSepherosa Ziehau 		}
342515516c77SSepherosa Ziehau 
342615516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
342715516c77SSepherosa Ziehau 		break;
342815516c77SSepherosa Ziehau 
342915516c77SSepherosa Ziehau 	case SIOCADDMULTI:
343015516c77SSepherosa Ziehau 	case SIOCDELMULTI:
343115516c77SSepherosa Ziehau 		HN_LOCK(sc);
343215516c77SSepherosa Ziehau 
343315516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
343415516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
343515516c77SSepherosa Ziehau 			break;
343615516c77SSepherosa Ziehau 		}
3437fdc4f478SSepherosa Ziehau 		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
3438fdc4f478SSepherosa Ziehau 			/*
3439fdc4f478SSepherosa Ziehau 			 * Multicast uses mutex; use busy-wait for
3440fdc4f478SSepherosa Ziehau 			 * the RNDIS reply.
3441fdc4f478SSepherosa Ziehau 			 */
3442fdc4f478SSepherosa Ziehau 			HN_NO_SLEEPING(sc);
3443c08f7b2cSSepherosa Ziehau 			hn_rxfilter_config(sc);
3444fdc4f478SSepherosa Ziehau 			HN_SLEEPING_OK(sc);
3445fdc4f478SSepherosa Ziehau 		}
344615516c77SSepherosa Ziehau 
34479c6cae24SSepherosa Ziehau 		/* XXX vlan(4) style mcast addr maintenance */
34489c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
34499c6cae24SSepherosa Ziehau 			int old_if_flags;
34509c6cae24SSepherosa Ziehau 
34519c6cae24SSepherosa Ziehau 			old_if_flags = sc->hn_vf_ifp->if_flags;
34529c6cae24SSepherosa Ziehau 			hn_xpnt_vf_saveifflags(sc);
34539c6cae24SSepherosa Ziehau 
34549c6cae24SSepherosa Ziehau 			if ((sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) &&
34559c6cae24SSepherosa Ziehau 			    ((old_if_flags ^ sc->hn_vf_ifp->if_flags) &
34569c6cae24SSepherosa Ziehau 			     IFF_ALLMULTI))
34579c6cae24SSepherosa Ziehau 				error = hn_xpnt_vf_iocsetflags(sc);
34589c6cae24SSepherosa Ziehau 		}
34599c6cae24SSepherosa Ziehau 
346015516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
346115516c77SSepherosa Ziehau 		break;
346215516c77SSepherosa Ziehau 
346315516c77SSepherosa Ziehau 	case SIOCSIFMEDIA:
346415516c77SSepherosa Ziehau 	case SIOCGIFMEDIA:
34659c6cae24SSepherosa Ziehau 		HN_LOCK(sc);
34669c6cae24SSepherosa Ziehau 		if (hn_xpnt_vf_isready(sc)) {
34679c6cae24SSepherosa Ziehau 			/*
34689c6cae24SSepherosa Ziehau 			 * SIOCGIFMEDIA expects ifmediareq, so don't
34699c6cae24SSepherosa Ziehau 			 * create and pass ifr_vf to the VF here; just
34709c6cae24SSepherosa Ziehau 			 * replace the ifr_name.
34719c6cae24SSepherosa Ziehau 			 */
34729c6cae24SSepherosa Ziehau 			vf_ifp = sc->hn_vf_ifp;
34739c6cae24SSepherosa Ziehau 			strlcpy(ifr->ifr_name, vf_ifp->if_xname,
34749c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
34759c6cae24SSepherosa Ziehau 			error = vf_ifp->if_ioctl(vf_ifp, cmd, data);
34769c6cae24SSepherosa Ziehau 			/* Restore the ifr_name. */
34779c6cae24SSepherosa Ziehau 			strlcpy(ifr->ifr_name, ifp->if_xname,
34789c6cae24SSepherosa Ziehau 			    sizeof(ifr->ifr_name));
34799c6cae24SSepherosa Ziehau 			HN_UNLOCK(sc);
34809c6cae24SSepherosa Ziehau 			break;
34819c6cae24SSepherosa Ziehau 		}
34829c6cae24SSepherosa Ziehau 		HN_UNLOCK(sc);
348315516c77SSepherosa Ziehau 		error = ifmedia_ioctl(ifp, ifr, &sc->hn_media, cmd);
348415516c77SSepherosa Ziehau 		break;
348515516c77SSepherosa Ziehau 
348615516c77SSepherosa Ziehau 	default:
348715516c77SSepherosa Ziehau 		error = ether_ioctl(ifp, cmd, data);
348815516c77SSepherosa Ziehau 		break;
348915516c77SSepherosa Ziehau 	}
349015516c77SSepherosa Ziehau 	return (error);
349115516c77SSepherosa Ziehau }
349215516c77SSepherosa Ziehau 
349315516c77SSepherosa Ziehau static void
34945bdfd3fdSDexuan Cui hn_stop(struct hn_softc *sc, bool detaching)
349515516c77SSepherosa Ziehau {
349615516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
349715516c77SSepherosa Ziehau 	int i;
349815516c77SSepherosa Ziehau 
349915516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
350015516c77SSepherosa Ziehau 
350115516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
350215516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
350315516c77SSepherosa Ziehau 
35049c6cae24SSepherosa Ziehau 	/* Clear RUNNING bit ASAP. */
35059c6cae24SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
35069c6cae24SSepherosa Ziehau 
35076c1204dfSSepherosa Ziehau 	/* Disable polling. */
35086c1204dfSSepherosa Ziehau 	hn_polling(sc, 0);
35096c1204dfSSepherosa Ziehau 
35109c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
35119c6cae24SSepherosa Ziehau 		KASSERT(sc->hn_vf_ifp != NULL,
35129c6cae24SSepherosa Ziehau 		    ("%s: VF is not attached", ifp->if_xname));
35139c6cae24SSepherosa Ziehau 
35149c6cae24SSepherosa Ziehau 		/* NOTE: hn_vf_lock for hn_transmit() */
35159c6cae24SSepherosa Ziehau 		rm_wlock(&sc->hn_vf_lock);
35169c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags &= ~HN_XVFFLAG_ENABLED;
35179c6cae24SSepherosa Ziehau 		rm_wunlock(&sc->hn_vf_lock);
35189c6cae24SSepherosa Ziehau 
35199c6cae24SSepherosa Ziehau 		/*
35209c6cae24SSepherosa Ziehau 		 * NOTE:
35219c6cae24SSepherosa Ziehau 		 * Datapath setting must happen _before_ bringing
35229c6cae24SSepherosa Ziehau 		 * the VF down.
35239c6cae24SSepherosa Ziehau 		 */
35249c6cae24SSepherosa Ziehau 		hn_nvs_set_datapath(sc, HN_NVS_DATAPATH_SYNTH);
35259c6cae24SSepherosa Ziehau 
35269c6cae24SSepherosa Ziehau 		/*
35279c6cae24SSepherosa Ziehau 		 * Bring the VF down.
35289c6cae24SSepherosa Ziehau 		 */
35299c6cae24SSepherosa Ziehau 		hn_xpnt_vf_saveifflags(sc);
35309c6cae24SSepherosa Ziehau 		sc->hn_vf_ifp->if_flags &= ~IFF_UP;
35319c6cae24SSepherosa Ziehau 		hn_xpnt_vf_iocsetflags(sc);
35329c6cae24SSepherosa Ziehau 	}
35339c6cae24SSepherosa Ziehau 
35349c6cae24SSepherosa Ziehau 	/* Suspend data transfers. */
353515516c77SSepherosa Ziehau 	hn_suspend_data(sc);
353615516c77SSepherosa Ziehau 
353715516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
353815516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
353915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
354015516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
35415bdfd3fdSDexuan Cui 
35425bdfd3fdSDexuan Cui 	/*
35439c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is active, make sure
35449c6cae24SSepherosa Ziehau 	 * that the RX filter still allows packet reception.
35455bdfd3fdSDexuan Cui 	 */
3546962f0357SSepherosa Ziehau 	if (!detaching && (sc->hn_flags & HN_FLAG_RXVF))
35475bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
354815516c77SSepherosa Ziehau }
354915516c77SSepherosa Ziehau 
355015516c77SSepherosa Ziehau static void
355115516c77SSepherosa Ziehau hn_init_locked(struct hn_softc *sc)
355215516c77SSepherosa Ziehau {
355315516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
355415516c77SSepherosa Ziehau 	int i;
355515516c77SSepherosa Ziehau 
355615516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
355715516c77SSepherosa Ziehau 
355815516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
355915516c77SSepherosa Ziehau 		return;
356015516c77SSepherosa Ziehau 
356115516c77SSepherosa Ziehau 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
356215516c77SSepherosa Ziehau 		return;
356315516c77SSepherosa Ziehau 
356415516c77SSepherosa Ziehau 	/* Configure RX filter */
3565c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
356615516c77SSepherosa Ziehau 
356715516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
356815516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
356915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
357015516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
357115516c77SSepherosa Ziehau 
357215516c77SSepherosa Ziehau 	/* Clear TX 'suspended' bit. */
357315516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_inuse);
357415516c77SSepherosa Ziehau 
35759c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
35769c6cae24SSepherosa Ziehau 		/* Initialize transparent VF. */
35779c6cae24SSepherosa Ziehau 		hn_xpnt_vf_init(sc);
35789c6cae24SSepherosa Ziehau 	}
35799c6cae24SSepherosa Ziehau 
358015516c77SSepherosa Ziehau 	/* Everything is ready; unleash! */
358115516c77SSepherosa Ziehau 	atomic_set_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
35826c1204dfSSepherosa Ziehau 
35836c1204dfSSepherosa Ziehau 	/* Re-enable polling if requested. */
35846c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz > 0)
35856c1204dfSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
358615516c77SSepherosa Ziehau }
358715516c77SSepherosa Ziehau 
358815516c77SSepherosa Ziehau static void
358915516c77SSepherosa Ziehau hn_init(void *xsc)
359015516c77SSepherosa Ziehau {
359115516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
359215516c77SSepherosa Ziehau 
359315516c77SSepherosa Ziehau 	HN_LOCK(sc);
359415516c77SSepherosa Ziehau 	hn_init_locked(sc);
359515516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
359615516c77SSepherosa Ziehau }
359715516c77SSepherosa Ziehau 
359815516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
359915516c77SSepherosa Ziehau 
360015516c77SSepherosa Ziehau static int
360115516c77SSepherosa Ziehau hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS)
360215516c77SSepherosa Ziehau {
360315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
360415516c77SSepherosa Ziehau 	unsigned int lenlim;
360515516c77SSepherosa Ziehau 	int error;
360615516c77SSepherosa Ziehau 
360715516c77SSepherosa Ziehau 	lenlim = sc->hn_rx_ring[0].hn_lro.lro_length_lim;
360815516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &lenlim, 0, req);
360915516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
361015516c77SSepherosa Ziehau 		return error;
361115516c77SSepherosa Ziehau 
361215516c77SSepherosa Ziehau 	HN_LOCK(sc);
361315516c77SSepherosa Ziehau 	if (lenlim < HN_LRO_LENLIM_MIN(sc->hn_ifp) ||
361415516c77SSepherosa Ziehau 	    lenlim > TCP_LRO_LENGTH_MAX) {
361515516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
361615516c77SSepherosa Ziehau 		return EINVAL;
361715516c77SSepherosa Ziehau 	}
361815516c77SSepherosa Ziehau 	hn_set_lro_lenlim(sc, lenlim);
361915516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
362015516c77SSepherosa Ziehau 
362115516c77SSepherosa Ziehau 	return 0;
362215516c77SSepherosa Ziehau }
362315516c77SSepherosa Ziehau 
362415516c77SSepherosa Ziehau static int
362515516c77SSepherosa Ziehau hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS)
362615516c77SSepherosa Ziehau {
362715516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
362815516c77SSepherosa Ziehau 	int ackcnt, error, i;
362915516c77SSepherosa Ziehau 
363015516c77SSepherosa Ziehau 	/*
363115516c77SSepherosa Ziehau 	 * lro_ackcnt_lim is append count limit,
363215516c77SSepherosa Ziehau 	 * +1 to turn it into aggregation limit.
363315516c77SSepherosa Ziehau 	 */
363415516c77SSepherosa Ziehau 	ackcnt = sc->hn_rx_ring[0].hn_lro.lro_ackcnt_lim + 1;
363515516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &ackcnt, 0, req);
363615516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
363715516c77SSepherosa Ziehau 		return error;
363815516c77SSepherosa Ziehau 
363915516c77SSepherosa Ziehau 	if (ackcnt < 2 || ackcnt > (TCP_LRO_ACKCNT_MAX + 1))
364015516c77SSepherosa Ziehau 		return EINVAL;
364115516c77SSepherosa Ziehau 
364215516c77SSepherosa Ziehau 	/*
364315516c77SSepherosa Ziehau 	 * Convert aggregation limit back to append
364415516c77SSepherosa Ziehau 	 * count limit.
364515516c77SSepherosa Ziehau 	 */
364615516c77SSepherosa Ziehau 	--ackcnt;
364715516c77SSepherosa Ziehau 	HN_LOCK(sc);
3648a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
364915516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_ackcnt_lim = ackcnt;
365015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
365115516c77SSepherosa Ziehau 	return 0;
365215516c77SSepherosa Ziehau }
365315516c77SSepherosa Ziehau 
365415516c77SSepherosa Ziehau #endif
365515516c77SSepherosa Ziehau 
365615516c77SSepherosa Ziehau static int
365715516c77SSepherosa Ziehau hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS)
365815516c77SSepherosa Ziehau {
365915516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
366015516c77SSepherosa Ziehau 	int hcsum = arg2;
366115516c77SSepherosa Ziehau 	int on, error, i;
366215516c77SSepherosa Ziehau 
366315516c77SSepherosa Ziehau 	on = 0;
366415516c77SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_trust_hcsum & hcsum)
366515516c77SSepherosa Ziehau 		on = 1;
366615516c77SSepherosa Ziehau 
366715516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &on, 0, req);
366815516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
366915516c77SSepherosa Ziehau 		return error;
367015516c77SSepherosa Ziehau 
367115516c77SSepherosa Ziehau 	HN_LOCK(sc);
3672a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
367315516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
367415516c77SSepherosa Ziehau 
367515516c77SSepherosa Ziehau 		if (on)
367615516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= hcsum;
367715516c77SSepherosa Ziehau 		else
367815516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum &= ~hcsum;
367915516c77SSepherosa Ziehau 	}
368015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
368115516c77SSepherosa Ziehau 	return 0;
368215516c77SSepherosa Ziehau }
368315516c77SSepherosa Ziehau 
368415516c77SSepherosa Ziehau static int
368515516c77SSepherosa Ziehau hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS)
368615516c77SSepherosa Ziehau {
368715516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
368815516c77SSepherosa Ziehau 	int chim_size, error;
368915516c77SSepherosa Ziehau 
369015516c77SSepherosa Ziehau 	chim_size = sc->hn_tx_ring[0].hn_chim_size;
369115516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &chim_size, 0, req);
369215516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
369315516c77SSepherosa Ziehau 		return error;
369415516c77SSepherosa Ziehau 
369515516c77SSepherosa Ziehau 	if (chim_size > sc->hn_chim_szmax || chim_size <= 0)
369615516c77SSepherosa Ziehau 		return EINVAL;
369715516c77SSepherosa Ziehau 
369815516c77SSepherosa Ziehau 	HN_LOCK(sc);
369915516c77SSepherosa Ziehau 	hn_set_chim_size(sc, chim_size);
370015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
370115516c77SSepherosa Ziehau 	return 0;
370215516c77SSepherosa Ziehau }
370315516c77SSepherosa Ziehau 
370415516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
370515516c77SSepherosa Ziehau static int
370615516c77SSepherosa Ziehau hn_rx_stat_int_sysctl(SYSCTL_HANDLER_ARGS)
370715516c77SSepherosa Ziehau {
370815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
370915516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
371015516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
371115516c77SSepherosa Ziehau 	uint64_t stat;
371215516c77SSepherosa Ziehau 
371315516c77SSepherosa Ziehau 	stat = 0;
371415516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
371515516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
371615516c77SSepherosa Ziehau 		stat += *((int *)((uint8_t *)rxr + ofs));
371715516c77SSepherosa Ziehau 	}
371815516c77SSepherosa Ziehau 
371915516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
372015516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
372115516c77SSepherosa Ziehau 		return error;
372215516c77SSepherosa Ziehau 
372315516c77SSepherosa Ziehau 	/* Zero out this stat. */
372415516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
372515516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
372615516c77SSepherosa Ziehau 		*((int *)((uint8_t *)rxr + ofs)) = 0;
372715516c77SSepherosa Ziehau 	}
372815516c77SSepherosa Ziehau 	return 0;
372915516c77SSepherosa Ziehau }
373015516c77SSepherosa Ziehau #else
373115516c77SSepherosa Ziehau static int
373215516c77SSepherosa Ziehau hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS)
373315516c77SSepherosa Ziehau {
373415516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
373515516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
373615516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
373715516c77SSepherosa Ziehau 	uint64_t stat;
373815516c77SSepherosa Ziehau 
373915516c77SSepherosa Ziehau 	stat = 0;
3740a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
374115516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
374215516c77SSepherosa Ziehau 		stat += *((uint64_t *)((uint8_t *)rxr + ofs));
374315516c77SSepherosa Ziehau 	}
374415516c77SSepherosa Ziehau 
374515516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
374615516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
374715516c77SSepherosa Ziehau 		return error;
374815516c77SSepherosa Ziehau 
374915516c77SSepherosa Ziehau 	/* Zero out this stat. */
3750a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
375115516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
375215516c77SSepherosa Ziehau 		*((uint64_t *)((uint8_t *)rxr + ofs)) = 0;
375315516c77SSepherosa Ziehau 	}
375415516c77SSepherosa Ziehau 	return 0;
375515516c77SSepherosa Ziehau }
375615516c77SSepherosa Ziehau 
375715516c77SSepherosa Ziehau #endif
375815516c77SSepherosa Ziehau 
375915516c77SSepherosa Ziehau static int
376015516c77SSepherosa Ziehau hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
376115516c77SSepherosa Ziehau {
376215516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
376315516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
376415516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
376515516c77SSepherosa Ziehau 	u_long stat;
376615516c77SSepherosa Ziehau 
376715516c77SSepherosa Ziehau 	stat = 0;
3768a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
376915516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
377015516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)rxr + ofs));
377115516c77SSepherosa Ziehau 	}
377215516c77SSepherosa Ziehau 
377315516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
377415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
377515516c77SSepherosa Ziehau 		return error;
377615516c77SSepherosa Ziehau 
377715516c77SSepherosa Ziehau 	/* Zero out this stat. */
3778a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
377915516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
378015516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)rxr + ofs)) = 0;
378115516c77SSepherosa Ziehau 	}
378215516c77SSepherosa Ziehau 	return 0;
378315516c77SSepherosa Ziehau }
378415516c77SSepherosa Ziehau 
378515516c77SSepherosa Ziehau static int
378615516c77SSepherosa Ziehau hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
378715516c77SSepherosa Ziehau {
378815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
378915516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
379015516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
379115516c77SSepherosa Ziehau 	u_long stat;
379215516c77SSepherosa Ziehau 
379315516c77SSepherosa Ziehau 	stat = 0;
3794a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
379515516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
379615516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)txr + ofs));
379715516c77SSepherosa Ziehau 	}
379815516c77SSepherosa Ziehau 
379915516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
380015516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
380115516c77SSepherosa Ziehau 		return error;
380215516c77SSepherosa Ziehau 
380315516c77SSepherosa Ziehau 	/* Zero out this stat. */
3804a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
380515516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
380615516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)txr + ofs)) = 0;
380715516c77SSepherosa Ziehau 	}
380815516c77SSepherosa Ziehau 	return 0;
380915516c77SSepherosa Ziehau }
381015516c77SSepherosa Ziehau 
381115516c77SSepherosa Ziehau static int
381215516c77SSepherosa Ziehau hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS)
381315516c77SSepherosa Ziehau {
381415516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
381515516c77SSepherosa Ziehau 	int ofs = arg2, i, error, conf;
381615516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
381715516c77SSepherosa Ziehau 
381815516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[0];
381915516c77SSepherosa Ziehau 	conf = *((int *)((uint8_t *)txr + ofs));
382015516c77SSepherosa Ziehau 
382115516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &conf, 0, req);
382215516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
382315516c77SSepherosa Ziehau 		return error;
382415516c77SSepherosa Ziehau 
382515516c77SSepherosa Ziehau 	HN_LOCK(sc);
3826a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
382715516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
382815516c77SSepherosa Ziehau 		*((int *)((uint8_t *)txr + ofs)) = conf;
382915516c77SSepherosa Ziehau 	}
383015516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
383115516c77SSepherosa Ziehau 
383215516c77SSepherosa Ziehau 	return 0;
383315516c77SSepherosa Ziehau }
383415516c77SSepherosa Ziehau 
383515516c77SSepherosa Ziehau static int
3836dc13fee6SSepherosa Ziehau hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS)
3837dc13fee6SSepherosa Ziehau {
3838dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3839dc13fee6SSepherosa Ziehau 	int error, size;
3840dc13fee6SSepherosa Ziehau 
3841dc13fee6SSepherosa Ziehau 	size = sc->hn_agg_size;
3842dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &size, 0, req);
3843dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
3844dc13fee6SSepherosa Ziehau 		return (error);
3845dc13fee6SSepherosa Ziehau 
3846dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
3847dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = size;
3848dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
3849dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
3850dc13fee6SSepherosa Ziehau 
3851dc13fee6SSepherosa Ziehau 	return (0);
3852dc13fee6SSepherosa Ziehau }
3853dc13fee6SSepherosa Ziehau 
3854dc13fee6SSepherosa Ziehau static int
3855dc13fee6SSepherosa Ziehau hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS)
3856dc13fee6SSepherosa Ziehau {
3857dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3858dc13fee6SSepherosa Ziehau 	int error, pkts;
3859dc13fee6SSepherosa Ziehau 
3860dc13fee6SSepherosa Ziehau 	pkts = sc->hn_agg_pkts;
3861dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pkts, 0, req);
3862dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
3863dc13fee6SSepherosa Ziehau 		return (error);
3864dc13fee6SSepherosa Ziehau 
3865dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
3866dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = pkts;
3867dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
3868dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
3869dc13fee6SSepherosa Ziehau 
3870dc13fee6SSepherosa Ziehau 	return (0);
3871dc13fee6SSepherosa Ziehau }
3872dc13fee6SSepherosa Ziehau 
3873dc13fee6SSepherosa Ziehau static int
3874dc13fee6SSepherosa Ziehau hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS)
3875dc13fee6SSepherosa Ziehau {
3876dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3877dc13fee6SSepherosa Ziehau 	int pkts;
3878dc13fee6SSepherosa Ziehau 
3879dc13fee6SSepherosa Ziehau 	pkts = sc->hn_tx_ring[0].hn_agg_pktmax;
3880dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &pkts, 0, req));
3881dc13fee6SSepherosa Ziehau }
3882dc13fee6SSepherosa Ziehau 
3883dc13fee6SSepherosa Ziehau static int
3884dc13fee6SSepherosa Ziehau hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS)
3885dc13fee6SSepherosa Ziehau {
3886dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3887dc13fee6SSepherosa Ziehau 	int align;
3888dc13fee6SSepherosa Ziehau 
3889dc13fee6SSepherosa Ziehau 	align = sc->hn_tx_ring[0].hn_agg_align;
3890dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &align, 0, req));
3891dc13fee6SSepherosa Ziehau }
3892dc13fee6SSepherosa Ziehau 
38936c1204dfSSepherosa Ziehau static void
38946c1204dfSSepherosa Ziehau hn_chan_polling(struct vmbus_channel *chan, u_int pollhz)
38956c1204dfSSepherosa Ziehau {
38966c1204dfSSepherosa Ziehau 	if (pollhz == 0)
38976c1204dfSSepherosa Ziehau 		vmbus_chan_poll_disable(chan);
38986c1204dfSSepherosa Ziehau 	else
38996c1204dfSSepherosa Ziehau 		vmbus_chan_poll_enable(chan, pollhz);
39006c1204dfSSepherosa Ziehau }
39016c1204dfSSepherosa Ziehau 
39026c1204dfSSepherosa Ziehau static void
39036c1204dfSSepherosa Ziehau hn_polling(struct hn_softc *sc, u_int pollhz)
39046c1204dfSSepherosa Ziehau {
39056c1204dfSSepherosa Ziehau 	int nsubch = sc->hn_rx_ring_inuse - 1;
39066c1204dfSSepherosa Ziehau 
39076c1204dfSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
39086c1204dfSSepherosa Ziehau 
39096c1204dfSSepherosa Ziehau 	if (nsubch > 0) {
39106c1204dfSSepherosa Ziehau 		struct vmbus_channel **subch;
39116c1204dfSSepherosa Ziehau 		int i;
39126c1204dfSSepherosa Ziehau 
39136c1204dfSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
39146c1204dfSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
39156c1204dfSSepherosa Ziehau 			hn_chan_polling(subch[i], pollhz);
39166c1204dfSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
39176c1204dfSSepherosa Ziehau 	}
39186c1204dfSSepherosa Ziehau 	hn_chan_polling(sc->hn_prichan, pollhz);
39196c1204dfSSepherosa Ziehau }
39206c1204dfSSepherosa Ziehau 
39216c1204dfSSepherosa Ziehau static int
39226c1204dfSSepherosa Ziehau hn_polling_sysctl(SYSCTL_HANDLER_ARGS)
39236c1204dfSSepherosa Ziehau {
39246c1204dfSSepherosa Ziehau 	struct hn_softc *sc = arg1;
39256c1204dfSSepherosa Ziehau 	int pollhz, error;
39266c1204dfSSepherosa Ziehau 
39276c1204dfSSepherosa Ziehau 	pollhz = sc->hn_pollhz;
39286c1204dfSSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pollhz, 0, req);
39296c1204dfSSepherosa Ziehau 	if (error || req->newptr == NULL)
39306c1204dfSSepherosa Ziehau 		return (error);
39316c1204dfSSepherosa Ziehau 
39326c1204dfSSepherosa Ziehau 	if (pollhz != 0 &&
39336c1204dfSSepherosa Ziehau 	    (pollhz < VMBUS_CHAN_POLLHZ_MIN || pollhz > VMBUS_CHAN_POLLHZ_MAX))
39346c1204dfSSepherosa Ziehau 		return (EINVAL);
39356c1204dfSSepherosa Ziehau 
39366c1204dfSSepherosa Ziehau 	HN_LOCK(sc);
39376c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz != pollhz) {
39386c1204dfSSepherosa Ziehau 		sc->hn_pollhz = pollhz;
39396c1204dfSSepherosa Ziehau 		if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) &&
39406c1204dfSSepherosa Ziehau 		    (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
39416c1204dfSSepherosa Ziehau 			hn_polling(sc, sc->hn_pollhz);
39426c1204dfSSepherosa Ziehau 	}
39436c1204dfSSepherosa Ziehau 	HN_UNLOCK(sc);
39446c1204dfSSepherosa Ziehau 
39456c1204dfSSepherosa Ziehau 	return (0);
39466c1204dfSSepherosa Ziehau }
39476c1204dfSSepherosa Ziehau 
3948dc13fee6SSepherosa Ziehau static int
394915516c77SSepherosa Ziehau hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS)
395015516c77SSepherosa Ziehau {
395115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
395215516c77SSepherosa Ziehau 	char verstr[16];
395315516c77SSepherosa Ziehau 
395415516c77SSepherosa Ziehau 	snprintf(verstr, sizeof(verstr), "%u.%u",
395515516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
395615516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
395715516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
395815516c77SSepherosa Ziehau }
395915516c77SSepherosa Ziehau 
396015516c77SSepherosa Ziehau static int
396115516c77SSepherosa Ziehau hn_caps_sysctl(SYSCTL_HANDLER_ARGS)
396215516c77SSepherosa Ziehau {
396315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
396415516c77SSepherosa Ziehau 	char caps_str[128];
396515516c77SSepherosa Ziehau 	uint32_t caps;
396615516c77SSepherosa Ziehau 
396715516c77SSepherosa Ziehau 	HN_LOCK(sc);
396815516c77SSepherosa Ziehau 	caps = sc->hn_caps;
396915516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
397015516c77SSepherosa Ziehau 	snprintf(caps_str, sizeof(caps_str), "%b", caps, HN_CAP_BITS);
397115516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, caps_str, sizeof(caps_str), req);
397215516c77SSepherosa Ziehau }
397315516c77SSepherosa Ziehau 
397415516c77SSepherosa Ziehau static int
397515516c77SSepherosa Ziehau hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS)
397615516c77SSepherosa Ziehau {
397715516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
397815516c77SSepherosa Ziehau 	char assist_str[128];
397915516c77SSepherosa Ziehau 	uint32_t hwassist;
398015516c77SSepherosa Ziehau 
398115516c77SSepherosa Ziehau 	HN_LOCK(sc);
398215516c77SSepherosa Ziehau 	hwassist = sc->hn_ifp->if_hwassist;
398315516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
398415516c77SSepherosa Ziehau 	snprintf(assist_str, sizeof(assist_str), "%b", hwassist, CSUM_BITS);
398515516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, assist_str, sizeof(assist_str), req);
398615516c77SSepherosa Ziehau }
398715516c77SSepherosa Ziehau 
398815516c77SSepherosa Ziehau static int
398915516c77SSepherosa Ziehau hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS)
399015516c77SSepherosa Ziehau {
399115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
399215516c77SSepherosa Ziehau 	char filter_str[128];
399315516c77SSepherosa Ziehau 	uint32_t filter;
399415516c77SSepherosa Ziehau 
399515516c77SSepherosa Ziehau 	HN_LOCK(sc);
399615516c77SSepherosa Ziehau 	filter = sc->hn_rx_filter;
399715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
399815516c77SSepherosa Ziehau 	snprintf(filter_str, sizeof(filter_str), "%b", filter,
399915516c77SSepherosa Ziehau 	    NDIS_PACKET_TYPES);
400015516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req);
400115516c77SSepherosa Ziehau }
400215516c77SSepherosa Ziehau 
400334d68912SSepherosa Ziehau #ifndef RSS
400434d68912SSepherosa Ziehau 
400515516c77SSepherosa Ziehau static int
400615516c77SSepherosa Ziehau hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS)
400715516c77SSepherosa Ziehau {
400815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
400915516c77SSepherosa Ziehau 	int error;
401015516c77SSepherosa Ziehau 
401115516c77SSepherosa Ziehau 	HN_LOCK(sc);
401215516c77SSepherosa Ziehau 
401315516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
401415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
401515516c77SSepherosa Ziehau 		goto back;
401615516c77SSepherosa Ziehau 
401715516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
401815516c77SSepherosa Ziehau 	if (error)
401915516c77SSepherosa Ziehau 		goto back;
402015516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
402115516c77SSepherosa Ziehau 
402215516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
402315516c77SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
402415516c77SSepherosa Ziehau 	} else {
402515516c77SSepherosa Ziehau 		/* Not RSS capable, at least for now; just save the RSS key. */
402615516c77SSepherosa Ziehau 		error = 0;
402715516c77SSepherosa Ziehau 	}
402815516c77SSepherosa Ziehau back:
402915516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
403015516c77SSepherosa Ziehau 	return (error);
403115516c77SSepherosa Ziehau }
403215516c77SSepherosa Ziehau 
403315516c77SSepherosa Ziehau static int
403415516c77SSepherosa Ziehau hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS)
403515516c77SSepherosa Ziehau {
403615516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
403715516c77SSepherosa Ziehau 	int error;
403815516c77SSepherosa Ziehau 
403915516c77SSepherosa Ziehau 	HN_LOCK(sc);
404015516c77SSepherosa Ziehau 
404115516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
404215516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
404315516c77SSepherosa Ziehau 		goto back;
404415516c77SSepherosa Ziehau 
404515516c77SSepherosa Ziehau 	/*
404615516c77SSepherosa Ziehau 	 * Don't allow RSS indirect table change, if this interface is not
404715516c77SSepherosa Ziehau 	 * RSS capable currently.
404815516c77SSepherosa Ziehau 	 */
404915516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
405015516c77SSepherosa Ziehau 		error = EOPNOTSUPP;
405115516c77SSepherosa Ziehau 		goto back;
405215516c77SSepherosa Ziehau 	}
405315516c77SSepherosa Ziehau 
405415516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
405515516c77SSepherosa Ziehau 	if (error)
405615516c77SSepherosa Ziehau 		goto back;
405715516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSIND;
405815516c77SSepherosa Ziehau 
4059afd4971bSSepherosa Ziehau 	hn_rss_ind_fixup(sc);
406015516c77SSepherosa Ziehau 	error = hn_rss_reconfig(sc);
406115516c77SSepherosa Ziehau back:
406215516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
406315516c77SSepherosa Ziehau 	return (error);
406415516c77SSepherosa Ziehau }
406515516c77SSepherosa Ziehau 
406634d68912SSepherosa Ziehau #endif	/* !RSS */
406734d68912SSepherosa Ziehau 
406815516c77SSepherosa Ziehau static int
406915516c77SSepherosa Ziehau hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS)
407015516c77SSepherosa Ziehau {
407115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
407215516c77SSepherosa Ziehau 	char hash_str[128];
407315516c77SSepherosa Ziehau 	uint32_t hash;
407415516c77SSepherosa Ziehau 
407515516c77SSepherosa Ziehau 	HN_LOCK(sc);
407615516c77SSepherosa Ziehau 	hash = sc->hn_rss_hash;
407715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
407815516c77SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
407915516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
408015516c77SSepherosa Ziehau }
408115516c77SSepherosa Ziehau 
408215516c77SSepherosa Ziehau static int
408340d60d6eSDexuan Cui hn_vf_sysctl(SYSCTL_HANDLER_ARGS)
408440d60d6eSDexuan Cui {
408540d60d6eSDexuan Cui 	struct hn_softc *sc = arg1;
4086499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
4087962f0357SSepherosa Ziehau 	struct ifnet *vf_ifp;
408840d60d6eSDexuan Cui 
408940d60d6eSDexuan Cui 	HN_LOCK(sc);
409040d60d6eSDexuan Cui 	vf_name[0] = '\0';
4091962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
4092962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
4093962f0357SSepherosa Ziehau 		snprintf(vf_name, sizeof(vf_name), "%s", vf_ifp->if_xname);
409440d60d6eSDexuan Cui 	HN_UNLOCK(sc);
409540d60d6eSDexuan Cui 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
409640d60d6eSDexuan Cui }
409740d60d6eSDexuan Cui 
409840d60d6eSDexuan Cui static int
4099499c3e17SSepherosa Ziehau hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS)
4100499c3e17SSepherosa Ziehau {
4101499c3e17SSepherosa Ziehau 	struct hn_softc *sc = arg1;
4102499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
4103962f0357SSepherosa Ziehau 	struct ifnet *vf_ifp;
4104499c3e17SSepherosa Ziehau 
4105499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
4106499c3e17SSepherosa Ziehau 	vf_name[0] = '\0';
4107962f0357SSepherosa Ziehau 	vf_ifp = sc->hn_rx_ring[0].hn_rxvf_ifp;
4108962f0357SSepherosa Ziehau 	if (vf_ifp != NULL)
4109962f0357SSepherosa Ziehau 		snprintf(vf_name, sizeof(vf_name), "%s", vf_ifp->if_xname);
4110499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
4111499c3e17SSepherosa Ziehau 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
4112499c3e17SSepherosa Ziehau }
4113499c3e17SSepherosa Ziehau 
4114499c3e17SSepherosa Ziehau static int
4115499c3e17SSepherosa Ziehau hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
4116499c3e17SSepherosa Ziehau {
4117499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4118499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4119499c3e17SSepherosa Ziehau 	int error, i;
4120499c3e17SSepherosa Ziehau 	bool first;
4121499c3e17SSepherosa Ziehau 
4122499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4123499c3e17SSepherosa Ziehau 	if (error != 0)
4124499c3e17SSepherosa Ziehau 		return (error);
4125499c3e17SSepherosa Ziehau 
4126499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4127499c3e17SSepherosa Ziehau 	if (sb == NULL)
4128499c3e17SSepherosa Ziehau 		return (ENOMEM);
4129499c3e17SSepherosa Ziehau 
4130499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4131499c3e17SSepherosa Ziehau 
4132499c3e17SSepherosa Ziehau 	first = true;
4133499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4134499c3e17SSepherosa Ziehau 		struct ifnet *ifp;
4135499c3e17SSepherosa Ziehau 
4136499c3e17SSepherosa Ziehau 		if (hn_vfmap[i] == NULL)
4137499c3e17SSepherosa Ziehau 			continue;
4138499c3e17SSepherosa Ziehau 
4139499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4140499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4141499c3e17SSepherosa Ziehau 			if (first)
4142499c3e17SSepherosa Ziehau 				sbuf_printf(sb, "%s", ifp->if_xname);
4143499c3e17SSepherosa Ziehau 			else
4144499c3e17SSepherosa Ziehau 				sbuf_printf(sb, " %s", ifp->if_xname);
4145499c3e17SSepherosa Ziehau 			first = false;
4146499c3e17SSepherosa Ziehau 		}
4147499c3e17SSepherosa Ziehau 	}
4148499c3e17SSepherosa Ziehau 
4149499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4150499c3e17SSepherosa Ziehau 
4151499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4152499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4153499c3e17SSepherosa Ziehau 	return (error);
4154499c3e17SSepherosa Ziehau }
4155499c3e17SSepherosa Ziehau 
4156499c3e17SSepherosa Ziehau static int
4157499c3e17SSepherosa Ziehau hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
4158499c3e17SSepherosa Ziehau {
4159499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
4160499c3e17SSepherosa Ziehau 	struct sbuf *sb;
4161499c3e17SSepherosa Ziehau 	int error, i;
4162499c3e17SSepherosa Ziehau 	bool first;
4163499c3e17SSepherosa Ziehau 
4164499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
4165499c3e17SSepherosa Ziehau 	if (error != 0)
4166499c3e17SSepherosa Ziehau 		return (error);
4167499c3e17SSepherosa Ziehau 
4168499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
4169499c3e17SSepherosa Ziehau 	if (sb == NULL)
4170499c3e17SSepherosa Ziehau 		return (ENOMEM);
4171499c3e17SSepherosa Ziehau 
4172499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
4173499c3e17SSepherosa Ziehau 
4174499c3e17SSepherosa Ziehau 	first = true;
4175499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
4176499c3e17SSepherosa Ziehau 		struct ifnet *ifp, *hn_ifp;
4177499c3e17SSepherosa Ziehau 
4178499c3e17SSepherosa Ziehau 		hn_ifp = hn_vfmap[i];
4179499c3e17SSepherosa Ziehau 		if (hn_ifp == NULL)
4180499c3e17SSepherosa Ziehau 			continue;
4181499c3e17SSepherosa Ziehau 
4182499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
4183499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
4184499c3e17SSepherosa Ziehau 			if (first) {
4185499c3e17SSepherosa Ziehau 				sbuf_printf(sb, "%s:%s", ifp->if_xname,
4186499c3e17SSepherosa Ziehau 				    hn_ifp->if_xname);
4187499c3e17SSepherosa Ziehau 			} else {
4188499c3e17SSepherosa Ziehau 				sbuf_printf(sb, " %s:%s", ifp->if_xname,
4189499c3e17SSepherosa Ziehau 				    hn_ifp->if_xname);
4190499c3e17SSepherosa Ziehau 			}
4191499c3e17SSepherosa Ziehau 			first = false;
4192499c3e17SSepherosa Ziehau 		}
4193499c3e17SSepherosa Ziehau 	}
4194499c3e17SSepherosa Ziehau 
4195499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
4196499c3e17SSepherosa Ziehau 
4197499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
4198499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
4199499c3e17SSepherosa Ziehau 	return (error);
4200499c3e17SSepherosa Ziehau }
4201499c3e17SSepherosa Ziehau 
4202499c3e17SSepherosa Ziehau static int
42039c6cae24SSepherosa Ziehau hn_xpnt_vf_accbpf_sysctl(SYSCTL_HANDLER_ARGS)
42049c6cae24SSepherosa Ziehau {
42059c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
42069c6cae24SSepherosa Ziehau 	int error, onoff = 0;
42079c6cae24SSepherosa Ziehau 
42089c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF)
42099c6cae24SSepherosa Ziehau 		onoff = 1;
42109c6cae24SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &onoff, 0, req);
42119c6cae24SSepherosa Ziehau 	if (error || req->newptr == NULL)
42129c6cae24SSepherosa Ziehau 		return (error);
42139c6cae24SSepherosa Ziehau 
42149c6cae24SSepherosa Ziehau 	HN_LOCK(sc);
42159c6cae24SSepherosa Ziehau 	/* NOTE: hn_vf_lock for hn_transmit() */
42169c6cae24SSepherosa Ziehau 	rm_wlock(&sc->hn_vf_lock);
42179c6cae24SSepherosa Ziehau 	if (onoff)
42189c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags |= HN_XVFFLAG_ACCBPF;
42199c6cae24SSepherosa Ziehau 	else
42209c6cae24SSepherosa Ziehau 		sc->hn_xvf_flags &= ~HN_XVFFLAG_ACCBPF;
42219c6cae24SSepherosa Ziehau 	rm_wunlock(&sc->hn_vf_lock);
42229c6cae24SSepherosa Ziehau 	HN_UNLOCK(sc);
42239c6cae24SSepherosa Ziehau 
42249c6cae24SSepherosa Ziehau 	return (0);
42259c6cae24SSepherosa Ziehau }
42269c6cae24SSepherosa Ziehau 
42279c6cae24SSepherosa Ziehau static int
42289c6cae24SSepherosa Ziehau hn_xpnt_vf_enabled_sysctl(SYSCTL_HANDLER_ARGS)
42299c6cae24SSepherosa Ziehau {
42309c6cae24SSepherosa Ziehau 	struct hn_softc *sc = arg1;
42319c6cae24SSepherosa Ziehau 	int enabled = 0;
42329c6cae24SSepherosa Ziehau 
42339c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
42349c6cae24SSepherosa Ziehau 		enabled = 1;
42359c6cae24SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &enabled, 0, req));
42369c6cae24SSepherosa Ziehau }
42379c6cae24SSepherosa Ziehau 
42389c6cae24SSepherosa Ziehau static int
423915516c77SSepherosa Ziehau hn_check_iplen(const struct mbuf *m, int hoff)
424015516c77SSepherosa Ziehau {
424115516c77SSepherosa Ziehau 	const struct ip *ip;
424215516c77SSepherosa Ziehau 	int len, iphlen, iplen;
424315516c77SSepherosa Ziehau 	const struct tcphdr *th;
424415516c77SSepherosa Ziehau 	int thoff;				/* TCP data offset */
424515516c77SSepherosa Ziehau 
424615516c77SSepherosa Ziehau 	len = hoff + sizeof(struct ip);
424715516c77SSepherosa Ziehau 
424815516c77SSepherosa Ziehau 	/* The packet must be at least the size of an IP header. */
424915516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < len)
425015516c77SSepherosa Ziehau 		return IPPROTO_DONE;
425115516c77SSepherosa Ziehau 
425215516c77SSepherosa Ziehau 	/* The fixed IP header must reside completely in the first mbuf. */
425315516c77SSepherosa Ziehau 	if (m->m_len < len)
425415516c77SSepherosa Ziehau 		return IPPROTO_DONE;
425515516c77SSepherosa Ziehau 
425615516c77SSepherosa Ziehau 	ip = mtodo(m, hoff);
425715516c77SSepherosa Ziehau 
425815516c77SSepherosa Ziehau 	/* Bound check the packet's stated IP header length. */
425915516c77SSepherosa Ziehau 	iphlen = ip->ip_hl << 2;
426015516c77SSepherosa Ziehau 	if (iphlen < sizeof(struct ip))		/* minimum header length */
426115516c77SSepherosa Ziehau 		return IPPROTO_DONE;
426215516c77SSepherosa Ziehau 
426315516c77SSepherosa Ziehau 	/* The full IP header must reside completely in the one mbuf. */
426415516c77SSepherosa Ziehau 	if (m->m_len < hoff + iphlen)
426515516c77SSepherosa Ziehau 		return IPPROTO_DONE;
426615516c77SSepherosa Ziehau 
426715516c77SSepherosa Ziehau 	iplen = ntohs(ip->ip_len);
426815516c77SSepherosa Ziehau 
426915516c77SSepherosa Ziehau 	/*
427015516c77SSepherosa Ziehau 	 * Check that the amount of data in the buffers is as
427115516c77SSepherosa Ziehau 	 * at least much as the IP header would have us expect.
427215516c77SSepherosa Ziehau 	 */
427315516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < hoff + iplen)
427415516c77SSepherosa Ziehau 		return IPPROTO_DONE;
427515516c77SSepherosa Ziehau 
427615516c77SSepherosa Ziehau 	/*
427715516c77SSepherosa Ziehau 	 * Ignore IP fragments.
427815516c77SSepherosa Ziehau 	 */
427915516c77SSepherosa Ziehau 	if (ntohs(ip->ip_off) & (IP_OFFMASK | IP_MF))
428015516c77SSepherosa Ziehau 		return IPPROTO_DONE;
428115516c77SSepherosa Ziehau 
428215516c77SSepherosa Ziehau 	/*
428315516c77SSepherosa Ziehau 	 * The TCP/IP or UDP/IP header must be entirely contained within
428415516c77SSepherosa Ziehau 	 * the first fragment of a packet.
428515516c77SSepherosa Ziehau 	 */
428615516c77SSepherosa Ziehau 	switch (ip->ip_p) {
428715516c77SSepherosa Ziehau 	case IPPROTO_TCP:
428815516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct tcphdr))
428915516c77SSepherosa Ziehau 			return IPPROTO_DONE;
429015516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct tcphdr))
429115516c77SSepherosa Ziehau 			return IPPROTO_DONE;
429215516c77SSepherosa Ziehau 		th = (const struct tcphdr *)((const uint8_t *)ip + iphlen);
429315516c77SSepherosa Ziehau 		thoff = th->th_off << 2;
429415516c77SSepherosa Ziehau 		if (thoff < sizeof(struct tcphdr) || thoff + iphlen > iplen)
429515516c77SSepherosa Ziehau 			return IPPROTO_DONE;
429615516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + thoff)
429715516c77SSepherosa Ziehau 			return IPPROTO_DONE;
429815516c77SSepherosa Ziehau 		break;
429915516c77SSepherosa Ziehau 	case IPPROTO_UDP:
430015516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct udphdr))
430115516c77SSepherosa Ziehau 			return IPPROTO_DONE;
430215516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct udphdr))
430315516c77SSepherosa Ziehau 			return IPPROTO_DONE;
430415516c77SSepherosa Ziehau 		break;
430515516c77SSepherosa Ziehau 	default:
430615516c77SSepherosa Ziehau 		if (iplen < iphlen)
430715516c77SSepherosa Ziehau 			return IPPROTO_DONE;
430815516c77SSepherosa Ziehau 		break;
430915516c77SSepherosa Ziehau 	}
431015516c77SSepherosa Ziehau 	return ip->ip_p;
431115516c77SSepherosa Ziehau }
431215516c77SSepherosa Ziehau 
431315516c77SSepherosa Ziehau static int
431415516c77SSepherosa Ziehau hn_create_rx_data(struct hn_softc *sc, int ring_cnt)
431515516c77SSepherosa Ziehau {
431615516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
431715516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
431815516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
431915516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
432015516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
432115516c77SSepherosa Ziehau 	int lroent_cnt;
432215516c77SSepherosa Ziehau #endif
432315516c77SSepherosa Ziehau #endif
432415516c77SSepherosa Ziehau 	int i;
432515516c77SSepherosa Ziehau 
432615516c77SSepherosa Ziehau 	/*
432715516c77SSepherosa Ziehau 	 * Create RXBUF for reception.
432815516c77SSepherosa Ziehau 	 *
432915516c77SSepherosa Ziehau 	 * NOTE:
433015516c77SSepherosa Ziehau 	 * - It is shared by all channels.
433115516c77SSepherosa Ziehau 	 * - A large enough buffer is allocated, certain version of NVSes
433215516c77SSepherosa Ziehau 	 *   may further limit the usable space.
433315516c77SSepherosa Ziehau 	 */
433415516c77SSepherosa Ziehau 	sc->hn_rxbuf = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
433515516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_RXBUF_SIZE, &sc->hn_rxbuf_dma,
433615516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
433715516c77SSepherosa Ziehau 	if (sc->hn_rxbuf == NULL) {
433815516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate rxbuf failed\n");
433915516c77SSepherosa Ziehau 		return (ENOMEM);
434015516c77SSepherosa Ziehau 	}
434115516c77SSepherosa Ziehau 
434215516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = ring_cnt;
434315516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = sc->hn_rx_ring_cnt;
434415516c77SSepherosa Ziehau 
434515516c77SSepherosa Ziehau 	sc->hn_rx_ring = malloc(sizeof(struct hn_rx_ring) * sc->hn_rx_ring_cnt,
434615516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
434715516c77SSepherosa Ziehau 
434815516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
434915516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
435015516c77SSepherosa Ziehau 	lroent_cnt = hn_lro_entry_count;
435115516c77SSepherosa Ziehau 	if (lroent_cnt < TCP_LRO_ENTRIES)
435215516c77SSepherosa Ziehau 		lroent_cnt = TCP_LRO_ENTRIES;
435315516c77SSepherosa Ziehau 	if (bootverbose)
435415516c77SSepherosa Ziehau 		device_printf(dev, "LRO: entry count %d\n", lroent_cnt);
435515516c77SSepherosa Ziehau #endif
435615516c77SSepherosa Ziehau #endif	/* INET || INET6 */
435715516c77SSepherosa Ziehau 
435815516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
435915516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
436015516c77SSepherosa Ziehau 
436115516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.rx sysctl tree */
436215516c77SSepherosa Ziehau 	sc->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rx",
436315516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
436415516c77SSepherosa Ziehau 
436515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
436615516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
436715516c77SSepherosa Ziehau 
436815516c77SSepherosa Ziehau 		rxr->hn_br = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
436915516c77SSepherosa Ziehau 		    PAGE_SIZE, 0, HN_TXBR_SIZE + HN_RXBR_SIZE,
437015516c77SSepherosa Ziehau 		    &rxr->hn_br_dma, BUS_DMA_WAITOK);
437115516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL) {
437215516c77SSepherosa Ziehau 			device_printf(dev, "allocate bufring failed\n");
437315516c77SSepherosa Ziehau 			return (ENOMEM);
437415516c77SSepherosa Ziehau 		}
437515516c77SSepherosa Ziehau 
437615516c77SSepherosa Ziehau 		if (hn_trust_hosttcp)
437715516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_TCP;
437815516c77SSepherosa Ziehau 		if (hn_trust_hostudp)
437915516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_UDP;
438015516c77SSepherosa Ziehau 		if (hn_trust_hostip)
438115516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_IP;
438215516c77SSepherosa Ziehau 		rxr->hn_ifp = sc->hn_ifp;
438315516c77SSepherosa Ziehau 		if (i < sc->hn_tx_ring_cnt)
438415516c77SSepherosa Ziehau 			rxr->hn_txr = &sc->hn_tx_ring[i];
438515516c77SSepherosa Ziehau 		rxr->hn_pktbuf_len = HN_PKTBUF_LEN_DEF;
438615516c77SSepherosa Ziehau 		rxr->hn_pktbuf = malloc(rxr->hn_pktbuf_len, M_DEVBUF, M_WAITOK);
438715516c77SSepherosa Ziehau 		rxr->hn_rx_idx = i;
438815516c77SSepherosa Ziehau 		rxr->hn_rxbuf = sc->hn_rxbuf;
438915516c77SSepherosa Ziehau 
439015516c77SSepherosa Ziehau 		/*
439115516c77SSepherosa Ziehau 		 * Initialize LRO.
439215516c77SSepherosa Ziehau 		 */
439315516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
439415516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
439515516c77SSepherosa Ziehau 		tcp_lro_init_args(&rxr->hn_lro, sc->hn_ifp, lroent_cnt,
439615516c77SSepherosa Ziehau 		    hn_lro_mbufq_depth);
439715516c77SSepherosa Ziehau #else
439815516c77SSepherosa Ziehau 		tcp_lro_init(&rxr->hn_lro);
439915516c77SSepherosa Ziehau 		rxr->hn_lro.ifp = sc->hn_ifp;
440015516c77SSepherosa Ziehau #endif
440115516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
440215516c77SSepherosa Ziehau 		rxr->hn_lro.lro_length_lim = HN_LRO_LENLIM_DEF;
440315516c77SSepherosa Ziehau 		rxr->hn_lro.lro_ackcnt_lim = HN_LRO_ACKCNT_DEF;
440415516c77SSepherosa Ziehau #endif
440515516c77SSepherosa Ziehau #endif	/* INET || INET6 */
440615516c77SSepherosa Ziehau 
440715516c77SSepherosa Ziehau 		if (sc->hn_rx_sysctl_tree != NULL) {
440815516c77SSepherosa Ziehau 			char name[16];
440915516c77SSepherosa Ziehau 
441015516c77SSepherosa Ziehau 			/*
441115516c77SSepherosa Ziehau 			 * Create per RX ring sysctl tree:
441215516c77SSepherosa Ziehau 			 * dev.hn.UNIT.rx.RINGID
441315516c77SSepherosa Ziehau 			 */
441415516c77SSepherosa Ziehau 			snprintf(name, sizeof(name), "%d", i);
441515516c77SSepherosa Ziehau 			rxr->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx,
441615516c77SSepherosa Ziehau 			    SYSCTL_CHILDREN(sc->hn_rx_sysctl_tree),
441715516c77SSepherosa Ziehau 			    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
441815516c77SSepherosa Ziehau 
441915516c77SSepherosa Ziehau 			if (rxr->hn_rx_sysctl_tree != NULL) {
442015516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
442115516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
442215516c77SSepherosa Ziehau 				    OID_AUTO, "packets", CTLFLAG_RW,
442315516c77SSepherosa Ziehau 				    &rxr->hn_pkts, "# of packets received");
442415516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
442515516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
442615516c77SSepherosa Ziehau 				    OID_AUTO, "rss_pkts", CTLFLAG_RW,
442715516c77SSepherosa Ziehau 				    &rxr->hn_rss_pkts,
442815516c77SSepherosa Ziehau 				    "# of packets w/ RSS info received");
442915516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx,
443015516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
443115516c77SSepherosa Ziehau 				    OID_AUTO, "pktbuf_len", CTLFLAG_RD,
443215516c77SSepherosa Ziehau 				    &rxr->hn_pktbuf_len, 0,
443315516c77SSepherosa Ziehau 				    "Temporary channel packet buffer length");
443415516c77SSepherosa Ziehau 			}
443515516c77SSepherosa Ziehau 		}
443615516c77SSepherosa Ziehau 	}
443715516c77SSepherosa Ziehau 
443815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_queued",
443915516c77SSepherosa Ziehau 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
444015516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_queued),
444115516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
444215516c77SSepherosa Ziehau 	    hn_rx_stat_int_sysctl,
444315516c77SSepherosa Ziehau #else
444415516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
444515516c77SSepherosa Ziehau #endif
444615516c77SSepherosa Ziehau 	    "LU", "LRO queued");
444715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_flushed",
444815516c77SSepherosa Ziehau 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
444915516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_flushed),
445015516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
445115516c77SSepherosa Ziehau 	    hn_rx_stat_int_sysctl,
445215516c77SSepherosa Ziehau #else
445315516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
445415516c77SSepherosa Ziehau #endif
445515516c77SSepherosa Ziehau 	    "LU", "LRO flushed");
445615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_tried",
445715516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
445815516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro_tried),
445915516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of LRO tries");
446015516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
446115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_length_lim",
446215516c77SSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
446315516c77SSepherosa Ziehau 	    hn_lro_lenlim_sysctl, "IU",
446415516c77SSepherosa Ziehau 	    "Max # of data bytes to be aggregated by LRO");
446515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_ackcnt_lim",
446615516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
446715516c77SSepherosa Ziehau 	    hn_lro_ackcnt_sysctl, "I",
446815516c77SSepherosa Ziehau 	    "Max # of ACKs to be aggregated by LRO");
446915516c77SSepherosa Ziehau #endif
447015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hosttcp",
447115516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_TCP,
447215516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
447315516c77SSepherosa Ziehau 	    "Trust tcp segement verification on host side, "
447415516c77SSepherosa Ziehau 	    "when csum info is missing");
447515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostudp",
447615516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_UDP,
447715516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
447815516c77SSepherosa Ziehau 	    "Trust udp datagram verification on host side, "
447915516c77SSepherosa Ziehau 	    "when csum info is missing");
448015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostip",
448115516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_IP,
448215516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
448315516c77SSepherosa Ziehau 	    "Trust ip packet verification on host side, "
448415516c77SSepherosa Ziehau 	    "when csum info is missing");
448515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_ip",
448615516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
448715516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_ip),
448815516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM IP");
448915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_tcp",
449015516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
449115516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_tcp),
449215516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM TCP");
449315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_udp",
449415516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
449515516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_udp),
449615516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM UDP");
449715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_trusted",
449815516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
449915516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_trusted),
450015516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU",
450115516c77SSepherosa Ziehau 	    "# of packets that we trust host's csum verification");
450215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "small_pkts",
450315516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
450415516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_small_pkts),
450515516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of small packets received");
450615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_ack_failed",
450715516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
450815516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_ack_failed),
450915516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of RXBUF ack failures");
451015516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_cnt",
451115516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_cnt, 0, "# created RX rings");
451215516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_inuse",
451315516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_inuse, 0, "# used RX rings");
451415516c77SSepherosa Ziehau 
451515516c77SSepherosa Ziehau 	return (0);
451615516c77SSepherosa Ziehau }
451715516c77SSepherosa Ziehau 
451815516c77SSepherosa Ziehau static void
451915516c77SSepherosa Ziehau hn_destroy_rx_data(struct hn_softc *sc)
452015516c77SSepherosa Ziehau {
452115516c77SSepherosa Ziehau 	int i;
452215516c77SSepherosa Ziehau 
452315516c77SSepherosa Ziehau 	if (sc->hn_rxbuf != NULL) {
45242494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXBUF_REF) == 0)
452515516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_rxbuf_dma, sc->hn_rxbuf);
45262494d735SSepherosa Ziehau 		else
45272494d735SSepherosa Ziehau 			device_printf(sc->hn_dev, "RXBUF is referenced\n");
452815516c77SSepherosa Ziehau 		sc->hn_rxbuf = NULL;
452915516c77SSepherosa Ziehau 	}
453015516c77SSepherosa Ziehau 
453115516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_cnt == 0)
453215516c77SSepherosa Ziehau 		return;
453315516c77SSepherosa Ziehau 
453415516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
453515516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
453615516c77SSepherosa Ziehau 
453715516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL)
453815516c77SSepherosa Ziehau 			continue;
45392494d735SSepherosa Ziehau 		if ((rxr->hn_rx_flags & HN_RX_FLAG_BR_REF) == 0) {
454015516c77SSepherosa Ziehau 			hyperv_dmamem_free(&rxr->hn_br_dma, rxr->hn_br);
45412494d735SSepherosa Ziehau 		} else {
45422494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
45432494d735SSepherosa Ziehau 			    "%dth channel bufring is referenced", i);
45442494d735SSepherosa Ziehau 		}
454515516c77SSepherosa Ziehau 		rxr->hn_br = NULL;
454615516c77SSepherosa Ziehau 
454715516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
454815516c77SSepherosa Ziehau 		tcp_lro_free(&rxr->hn_lro);
454915516c77SSepherosa Ziehau #endif
455015516c77SSepherosa Ziehau 		free(rxr->hn_pktbuf, M_DEVBUF);
455115516c77SSepherosa Ziehau 	}
455215516c77SSepherosa Ziehau 	free(sc->hn_rx_ring, M_DEVBUF);
455315516c77SSepherosa Ziehau 	sc->hn_rx_ring = NULL;
455415516c77SSepherosa Ziehau 
455515516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = 0;
455615516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = 0;
455715516c77SSepherosa Ziehau }
455815516c77SSepherosa Ziehau 
455915516c77SSepherosa Ziehau static int
456015516c77SSepherosa Ziehau hn_tx_ring_create(struct hn_softc *sc, int id)
456115516c77SSepherosa Ziehau {
456215516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[id];
456315516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
456415516c77SSepherosa Ziehau 	bus_dma_tag_t parent_dtag;
456515516c77SSepherosa Ziehau 	int error, i;
456615516c77SSepherosa Ziehau 
456715516c77SSepherosa Ziehau 	txr->hn_sc = sc;
456815516c77SSepherosa Ziehau 	txr->hn_tx_idx = id;
456915516c77SSepherosa Ziehau 
457015516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
457115516c77SSepherosa Ziehau 	mtx_init(&txr->hn_txlist_spin, "hn txlist", NULL, MTX_SPIN);
457215516c77SSepherosa Ziehau #endif
457315516c77SSepherosa Ziehau 	mtx_init(&txr->hn_tx_lock, "hn tx", NULL, MTX_DEF);
457415516c77SSepherosa Ziehau 
457515516c77SSepherosa Ziehau 	txr->hn_txdesc_cnt = HN_TX_DESC_CNT;
457615516c77SSepherosa Ziehau 	txr->hn_txdesc = malloc(sizeof(struct hn_txdesc) * txr->hn_txdesc_cnt,
457715516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
457815516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
457915516c77SSepherosa Ziehau 	SLIST_INIT(&txr->hn_txlist);
458015516c77SSepherosa Ziehau #else
458115516c77SSepherosa Ziehau 	txr->hn_txdesc_br = buf_ring_alloc(txr->hn_txdesc_cnt, M_DEVBUF,
458215516c77SSepherosa Ziehau 	    M_WAITOK, &txr->hn_tx_lock);
458315516c77SSepherosa Ziehau #endif
458415516c77SSepherosa Ziehau 
45850e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_EVTTQ) {
45860e11868dSSepherosa Ziehau 		txr->hn_tx_taskq = VMBUS_GET_EVENT_TASKQ(
45870e11868dSSepherosa Ziehau 		    device_get_parent(dev), dev, HN_RING_IDX2CPU(sc, id));
45880e11868dSSepherosa Ziehau 	} else {
4589fdd0222aSSepherosa Ziehau 		txr->hn_tx_taskq = sc->hn_tx_taskqs[id % hn_tx_taskq_cnt];
45900e11868dSSepherosa Ziehau 	}
459115516c77SSepherosa Ziehau 
459223bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
459315516c77SSepherosa Ziehau 	if (hn_use_if_start) {
459415516c77SSepherosa Ziehau 		txr->hn_txeof = hn_start_txeof;
459515516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_start_taskfunc, txr);
459615516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_start_txeof_taskfunc, txr);
459723bf9e15SSepherosa Ziehau 	} else
459823bf9e15SSepherosa Ziehau #endif
459923bf9e15SSepherosa Ziehau 	{
460015516c77SSepherosa Ziehau 		int br_depth;
460115516c77SSepherosa Ziehau 
460215516c77SSepherosa Ziehau 		txr->hn_txeof = hn_xmit_txeof;
460315516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_xmit_taskfunc, txr);
460415516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_xmit_txeof_taskfunc, txr);
460515516c77SSepherosa Ziehau 
460615516c77SSepherosa Ziehau 		br_depth = hn_get_txswq_depth(txr);
460715516c77SSepherosa Ziehau 		txr->hn_mbuf_br = buf_ring_alloc(br_depth, M_DEVBUF,
460815516c77SSepherosa Ziehau 		    M_WAITOK, &txr->hn_tx_lock);
460915516c77SSepherosa Ziehau 	}
461015516c77SSepherosa Ziehau 
461115516c77SSepherosa Ziehau 	txr->hn_direct_tx_size = hn_direct_tx_size;
461215516c77SSepherosa Ziehau 
461315516c77SSepherosa Ziehau 	/*
461415516c77SSepherosa Ziehau 	 * Always schedule transmission instead of trying to do direct
461515516c77SSepherosa Ziehau 	 * transmission.  This one gives the best performance so far.
461615516c77SSepherosa Ziehau 	 */
461715516c77SSepherosa Ziehau 	txr->hn_sched_tx = 1;
461815516c77SSepherosa Ziehau 
461915516c77SSepherosa Ziehau 	parent_dtag = bus_get_dma_tag(dev);
462015516c77SSepherosa Ziehau 
462115516c77SSepherosa Ziehau 	/* DMA tag for RNDIS packet messages. */
462215516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
462315516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_ALIGN,		/* alignment */
462415516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_BOUNDARY,	/* boundary */
462515516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
462615516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
462715516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
462815516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsize */
462915516c77SSepherosa Ziehau 	    1,				/* nsegments */
463015516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsegsize */
463115516c77SSepherosa Ziehau 	    0,				/* flags */
463215516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
463315516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
463415516c77SSepherosa Ziehau 	    &txr->hn_tx_rndis_dtag);
463515516c77SSepherosa Ziehau 	if (error) {
463615516c77SSepherosa Ziehau 		device_printf(dev, "failed to create rndis dmatag\n");
463715516c77SSepherosa Ziehau 		return error;
463815516c77SSepherosa Ziehau 	}
463915516c77SSepherosa Ziehau 
464015516c77SSepherosa Ziehau 	/* DMA tag for data. */
464115516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
464215516c77SSepherosa Ziehau 	    1,				/* alignment */
464315516c77SSepherosa Ziehau 	    HN_TX_DATA_BOUNDARY,	/* boundary */
464415516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
464515516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
464615516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
464715516c77SSepherosa Ziehau 	    HN_TX_DATA_MAXSIZE,		/* maxsize */
464815516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGCNT_MAX,	/* nsegments */
464915516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGSIZE,		/* maxsegsize */
465015516c77SSepherosa Ziehau 	    0,				/* flags */
465115516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
465215516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
465315516c77SSepherosa Ziehau 	    &txr->hn_tx_data_dtag);
465415516c77SSepherosa Ziehau 	if (error) {
465515516c77SSepherosa Ziehau 		device_printf(dev, "failed to create data dmatag\n");
465615516c77SSepherosa Ziehau 		return error;
465715516c77SSepherosa Ziehau 	}
465815516c77SSepherosa Ziehau 
465915516c77SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i) {
466015516c77SSepherosa Ziehau 		struct hn_txdesc *txd = &txr->hn_txdesc[i];
466115516c77SSepherosa Ziehau 
466215516c77SSepherosa Ziehau 		txd->txr = txr;
466315516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
4664dc13fee6SSepherosa Ziehau 		STAILQ_INIT(&txd->agg_list);
466515516c77SSepherosa Ziehau 
466615516c77SSepherosa Ziehau 		/*
466715516c77SSepherosa Ziehau 		 * Allocate and load RNDIS packet message.
466815516c77SSepherosa Ziehau 		 */
466915516c77SSepherosa Ziehau         	error = bus_dmamem_alloc(txr->hn_tx_rndis_dtag,
467015516c77SSepherosa Ziehau 		    (void **)&txd->rndis_pkt,
467115516c77SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
467215516c77SSepherosa Ziehau 		    &txd->rndis_pkt_dmap);
467315516c77SSepherosa Ziehau 		if (error) {
467415516c77SSepherosa Ziehau 			device_printf(dev,
467515516c77SSepherosa Ziehau 			    "failed to allocate rndis_packet_msg, %d\n", i);
467615516c77SSepherosa Ziehau 			return error;
467715516c77SSepherosa Ziehau 		}
467815516c77SSepherosa Ziehau 
467915516c77SSepherosa Ziehau 		error = bus_dmamap_load(txr->hn_tx_rndis_dtag,
468015516c77SSepherosa Ziehau 		    txd->rndis_pkt_dmap,
468115516c77SSepherosa Ziehau 		    txd->rndis_pkt, HN_RNDIS_PKT_LEN,
468215516c77SSepherosa Ziehau 		    hyperv_dma_map_paddr, &txd->rndis_pkt_paddr,
468315516c77SSepherosa Ziehau 		    BUS_DMA_NOWAIT);
468415516c77SSepherosa Ziehau 		if (error) {
468515516c77SSepherosa Ziehau 			device_printf(dev,
468615516c77SSepherosa Ziehau 			    "failed to load rndis_packet_msg, %d\n", i);
468715516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
468815516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
468915516c77SSepherosa Ziehau 			return error;
469015516c77SSepherosa Ziehau 		}
469115516c77SSepherosa Ziehau 
469215516c77SSepherosa Ziehau 		/* DMA map for TX data. */
469315516c77SSepherosa Ziehau 		error = bus_dmamap_create(txr->hn_tx_data_dtag, 0,
469415516c77SSepherosa Ziehau 		    &txd->data_dmap);
469515516c77SSepherosa Ziehau 		if (error) {
469615516c77SSepherosa Ziehau 			device_printf(dev,
469715516c77SSepherosa Ziehau 			    "failed to allocate tx data dmamap\n");
469815516c77SSepherosa Ziehau 			bus_dmamap_unload(txr->hn_tx_rndis_dtag,
469915516c77SSepherosa Ziehau 			    txd->rndis_pkt_dmap);
470015516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
470115516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
470215516c77SSepherosa Ziehau 			return error;
470315516c77SSepherosa Ziehau 		}
470415516c77SSepherosa Ziehau 
470515516c77SSepherosa Ziehau 		/* All set, put it to list */
470615516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_ONLIST;
470715516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
470815516c77SSepherosa Ziehau 		SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
470915516c77SSepherosa Ziehau #else
471015516c77SSepherosa Ziehau 		buf_ring_enqueue(txr->hn_txdesc_br, txd);
471115516c77SSepherosa Ziehau #endif
471215516c77SSepherosa Ziehau 	}
471315516c77SSepherosa Ziehau 	txr->hn_txdesc_avail = txr->hn_txdesc_cnt;
471415516c77SSepherosa Ziehau 
471515516c77SSepherosa Ziehau 	if (sc->hn_tx_sysctl_tree != NULL) {
471615516c77SSepherosa Ziehau 		struct sysctl_oid_list *child;
471715516c77SSepherosa Ziehau 		struct sysctl_ctx_list *ctx;
471815516c77SSepherosa Ziehau 		char name[16];
471915516c77SSepherosa Ziehau 
472015516c77SSepherosa Ziehau 		/*
472115516c77SSepherosa Ziehau 		 * Create per TX ring sysctl tree:
472215516c77SSepherosa Ziehau 		 * dev.hn.UNIT.tx.RINGID
472315516c77SSepherosa Ziehau 		 */
472415516c77SSepherosa Ziehau 		ctx = device_get_sysctl_ctx(dev);
472515516c77SSepherosa Ziehau 		child = SYSCTL_CHILDREN(sc->hn_tx_sysctl_tree);
472615516c77SSepherosa Ziehau 
472715516c77SSepherosa Ziehau 		snprintf(name, sizeof(name), "%d", id);
472815516c77SSepherosa Ziehau 		txr->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
472915516c77SSepherosa Ziehau 		    name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
473015516c77SSepherosa Ziehau 
473115516c77SSepherosa Ziehau 		if (txr->hn_tx_sysctl_tree != NULL) {
473215516c77SSepherosa Ziehau 			child = SYSCTL_CHILDREN(txr->hn_tx_sysctl_tree);
473315516c77SSepherosa Ziehau 
473485e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
473515516c77SSepherosa Ziehau 			SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_avail",
473615516c77SSepherosa Ziehau 			    CTLFLAG_RD, &txr->hn_txdesc_avail, 0,
473715516c77SSepherosa Ziehau 			    "# of available TX descs");
473885e4ae1eSSepherosa Ziehau #endif
473923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
474023bf9e15SSepherosa Ziehau 			if (!hn_use_if_start)
474123bf9e15SSepherosa Ziehau #endif
474223bf9e15SSepherosa Ziehau 			{
474315516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx, child, OID_AUTO, "oactive",
474415516c77SSepherosa Ziehau 				    CTLFLAG_RD, &txr->hn_oactive, 0,
474515516c77SSepherosa Ziehau 				    "over active");
474615516c77SSepherosa Ziehau 			}
474715516c77SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "packets",
474815516c77SSepherosa Ziehau 			    CTLFLAG_RW, &txr->hn_pkts,
474915516c77SSepherosa Ziehau 			    "# of packets transmitted");
4750dc13fee6SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "sends",
4751dc13fee6SSepherosa Ziehau 			    CTLFLAG_RW, &txr->hn_sends, "# of sends");
475215516c77SSepherosa Ziehau 		}
475315516c77SSepherosa Ziehau 	}
475415516c77SSepherosa Ziehau 
475515516c77SSepherosa Ziehau 	return 0;
475615516c77SSepherosa Ziehau }
475715516c77SSepherosa Ziehau 
475815516c77SSepherosa Ziehau static void
475915516c77SSepherosa Ziehau hn_txdesc_dmamap_destroy(struct hn_txdesc *txd)
476015516c77SSepherosa Ziehau {
476115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = txd->txr;
476215516c77SSepherosa Ziehau 
476315516c77SSepherosa Ziehau 	KASSERT(txd->m == NULL, ("still has mbuf installed"));
476415516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("still dma mapped"));
476515516c77SSepherosa Ziehau 
476615516c77SSepherosa Ziehau 	bus_dmamap_unload(txr->hn_tx_rndis_dtag, txd->rndis_pkt_dmap);
476715516c77SSepherosa Ziehau 	bus_dmamem_free(txr->hn_tx_rndis_dtag, txd->rndis_pkt,
476815516c77SSepherosa Ziehau 	    txd->rndis_pkt_dmap);
476915516c77SSepherosa Ziehau 	bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap);
477015516c77SSepherosa Ziehau }
477115516c77SSepherosa Ziehau 
477215516c77SSepherosa Ziehau static void
477325641fc7SSepherosa Ziehau hn_txdesc_gc(struct hn_tx_ring *txr, struct hn_txdesc *txd)
477425641fc7SSepherosa Ziehau {
477525641fc7SSepherosa Ziehau 
477625641fc7SSepherosa Ziehau 	KASSERT(txd->refs == 0 || txd->refs == 1,
477725641fc7SSepherosa Ziehau 	    ("invalid txd refs %d", txd->refs));
477825641fc7SSepherosa Ziehau 
477925641fc7SSepherosa Ziehau 	/* Aggregated txds will be freed by their aggregating txd. */
478025641fc7SSepherosa Ziehau 	if (txd->refs > 0 && (txd->flags & HN_TXD_FLAG_ONAGG) == 0) {
478125641fc7SSepherosa Ziehau 		int freed;
478225641fc7SSepherosa Ziehau 
478325641fc7SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
478425641fc7SSepherosa Ziehau 		KASSERT(freed, ("can't free txdesc"));
478525641fc7SSepherosa Ziehau 	}
478625641fc7SSepherosa Ziehau }
478725641fc7SSepherosa Ziehau 
478825641fc7SSepherosa Ziehau static void
478915516c77SSepherosa Ziehau hn_tx_ring_destroy(struct hn_tx_ring *txr)
479015516c77SSepherosa Ziehau {
479125641fc7SSepherosa Ziehau 	int i;
479215516c77SSepherosa Ziehau 
479315516c77SSepherosa Ziehau 	if (txr->hn_txdesc == NULL)
479415516c77SSepherosa Ziehau 		return;
479515516c77SSepherosa Ziehau 
479625641fc7SSepherosa Ziehau 	/*
479725641fc7SSepherosa Ziehau 	 * NOTE:
479825641fc7SSepherosa Ziehau 	 * Because the freeing of aggregated txds will be deferred
479925641fc7SSepherosa Ziehau 	 * to the aggregating txd, two passes are used here:
480025641fc7SSepherosa Ziehau 	 * - The first pass GCes any pending txds.  This GC is necessary,
480125641fc7SSepherosa Ziehau 	 *   since if the channels are revoked, hypervisor will not
480225641fc7SSepherosa Ziehau 	 *   deliver send-done for all pending txds.
480325641fc7SSepherosa Ziehau 	 * - The second pass frees the busdma stuffs, i.e. after all txds
480425641fc7SSepherosa Ziehau 	 *   were freed.
480525641fc7SSepherosa Ziehau 	 */
480625641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
480725641fc7SSepherosa Ziehau 		hn_txdesc_gc(txr, &txr->hn_txdesc[i]);
480825641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
480925641fc7SSepherosa Ziehau 		hn_txdesc_dmamap_destroy(&txr->hn_txdesc[i]);
481015516c77SSepherosa Ziehau 
481115516c77SSepherosa Ziehau 	if (txr->hn_tx_data_dtag != NULL)
481215516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_data_dtag);
481315516c77SSepherosa Ziehau 	if (txr->hn_tx_rndis_dtag != NULL)
481415516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_rndis_dtag);
481515516c77SSepherosa Ziehau 
481615516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
481715516c77SSepherosa Ziehau 	buf_ring_free(txr->hn_txdesc_br, M_DEVBUF);
481815516c77SSepherosa Ziehau #endif
481915516c77SSepherosa Ziehau 
482015516c77SSepherosa Ziehau 	free(txr->hn_txdesc, M_DEVBUF);
482115516c77SSepherosa Ziehau 	txr->hn_txdesc = NULL;
482215516c77SSepherosa Ziehau 
482315516c77SSepherosa Ziehau 	if (txr->hn_mbuf_br != NULL)
482415516c77SSepherosa Ziehau 		buf_ring_free(txr->hn_mbuf_br, M_DEVBUF);
482515516c77SSepherosa Ziehau 
482615516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
482715516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_txlist_spin);
482815516c77SSepherosa Ziehau #endif
482915516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_tx_lock);
483015516c77SSepherosa Ziehau }
483115516c77SSepherosa Ziehau 
483215516c77SSepherosa Ziehau static int
483315516c77SSepherosa Ziehau hn_create_tx_data(struct hn_softc *sc, int ring_cnt)
483415516c77SSepherosa Ziehau {
483515516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
483615516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
483715516c77SSepherosa Ziehau 	int i;
483815516c77SSepherosa Ziehau 
483915516c77SSepherosa Ziehau 	/*
484015516c77SSepherosa Ziehau 	 * Create TXBUF for chimney sending.
484115516c77SSepherosa Ziehau 	 *
484215516c77SSepherosa Ziehau 	 * NOTE: It is shared by all channels.
484315516c77SSepherosa Ziehau 	 */
484415516c77SSepherosa Ziehau 	sc->hn_chim = hyperv_dmamem_alloc(bus_get_dma_tag(sc->hn_dev),
484515516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_CHIM_SIZE, &sc->hn_chim_dma,
484615516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
484715516c77SSepherosa Ziehau 	if (sc->hn_chim == NULL) {
484815516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate txbuf failed\n");
484915516c77SSepherosa Ziehau 		return (ENOMEM);
485015516c77SSepherosa Ziehau 	}
485115516c77SSepherosa Ziehau 
485215516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = ring_cnt;
485315516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
485415516c77SSepherosa Ziehau 
485515516c77SSepherosa Ziehau 	sc->hn_tx_ring = malloc(sizeof(struct hn_tx_ring) * sc->hn_tx_ring_cnt,
485615516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
485715516c77SSepherosa Ziehau 
485815516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(sc->hn_dev);
485915516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->hn_dev));
486015516c77SSepherosa Ziehau 
486115516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.tx sysctl tree */
486215516c77SSepherosa Ziehau 	sc->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tx",
486315516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
486415516c77SSepherosa Ziehau 
486515516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
486615516c77SSepherosa Ziehau 		int error;
486715516c77SSepherosa Ziehau 
486815516c77SSepherosa Ziehau 		error = hn_tx_ring_create(sc, i);
486915516c77SSepherosa Ziehau 		if (error)
487015516c77SSepherosa Ziehau 			return error;
487115516c77SSepherosa Ziehau 	}
487215516c77SSepherosa Ziehau 
487315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "no_txdescs",
487415516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
487515516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_no_txdescs),
487615516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of times short of TX descs");
487715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "send_failed",
487815516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
487915516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_send_failed),
488015516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of hyper-v sending failure");
488115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdma_failed",
488215516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
488315516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_txdma_failed),
488415516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX DMA failure");
4885dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_flush_failed",
4886dc13fee6SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
4887dc13fee6SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_flush_failed),
4888dc13fee6SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU",
4889dc13fee6SSepherosa Ziehau 	    "# of packet transmission aggregation flush failure");
489015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_collapsed",
489115516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
489215516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_collapsed),
489315516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX mbuf collapsed");
489415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney",
489515516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
489615516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney),
489715516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send");
489815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_tried",
489915516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
490015516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney_tried),
490115516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send tries");
490215516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_cnt",
490315516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_txdesc_cnt, 0,
490415516c77SSepherosa Ziehau 	    "# of total TX descs");
490515516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_chimney_max",
490615516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_chim_szmax, 0,
490715516c77SSepherosa Ziehau 	    "Chimney send packet size upper boundary");
490815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_size",
490915516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
491015516c77SSepherosa Ziehau 	    hn_chim_size_sysctl, "I", "Chimney send packet size limit");
491115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "direct_tx_size",
491215516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
491315516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_direct_tx_size),
491415516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
491515516c77SSepherosa Ziehau 	    "Size of the packet for direct transmission");
491615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "sched_tx",
491715516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
491815516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_sched_tx),
491915516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
492015516c77SSepherosa Ziehau 	    "Always schedule transmission "
492115516c77SSepherosa Ziehau 	    "instead of doing direct transmission");
492215516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_cnt",
492315516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_cnt, 0, "# created TX rings");
492415516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_inuse",
492515516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_inuse, 0, "# used TX rings");
4926dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "agg_szmax",
4927dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_agg_szmax, 0,
4928dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation size");
4929dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pktmax",
4930dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
4931dc13fee6SSepherosa Ziehau 	    hn_txagg_pktmax_sysctl, "I",
4932dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation packets");
4933dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_align",
4934dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
4935dc13fee6SSepherosa Ziehau 	    hn_txagg_align_sysctl, "I",
4936dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation alignment");
493715516c77SSepherosa Ziehau 
493815516c77SSepherosa Ziehau 	return 0;
493915516c77SSepherosa Ziehau }
494015516c77SSepherosa Ziehau 
494115516c77SSepherosa Ziehau static void
494215516c77SSepherosa Ziehau hn_set_chim_size(struct hn_softc *sc, int chim_size)
494315516c77SSepherosa Ziehau {
494415516c77SSepherosa Ziehau 	int i;
494515516c77SSepherosa Ziehau 
4946a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
494715516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_chim_size = chim_size;
494815516c77SSepherosa Ziehau }
494915516c77SSepherosa Ziehau 
495015516c77SSepherosa Ziehau static void
495115516c77SSepherosa Ziehau hn_set_tso_maxsize(struct hn_softc *sc, int tso_maxlen, int mtu)
495215516c77SSepherosa Ziehau {
495315516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
49549c6cae24SSepherosa Ziehau 	u_int hw_tsomax;
495515516c77SSepherosa Ziehau 	int tso_minlen;
495615516c77SSepherosa Ziehau 
49579c6cae24SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
49589c6cae24SSepherosa Ziehau 
495915516c77SSepherosa Ziehau 	if ((ifp->if_capabilities & (IFCAP_TSO4 | IFCAP_TSO6)) == 0)
496015516c77SSepherosa Ziehau 		return;
496115516c77SSepherosa Ziehau 
496215516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_sgmin >= 2,
496315516c77SSepherosa Ziehau 	    ("invalid NDIS tso sgmin %d", sc->hn_ndis_tso_sgmin));
496415516c77SSepherosa Ziehau 	tso_minlen = sc->hn_ndis_tso_sgmin * mtu;
496515516c77SSepherosa Ziehau 
496615516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_szmax >= tso_minlen &&
496715516c77SSepherosa Ziehau 	    sc->hn_ndis_tso_szmax <= IP_MAXPACKET,
496815516c77SSepherosa Ziehau 	    ("invalid NDIS tso szmax %d", sc->hn_ndis_tso_szmax));
496915516c77SSepherosa Ziehau 
497015516c77SSepherosa Ziehau 	if (tso_maxlen < tso_minlen)
497115516c77SSepherosa Ziehau 		tso_maxlen = tso_minlen;
497215516c77SSepherosa Ziehau 	else if (tso_maxlen > IP_MAXPACKET)
497315516c77SSepherosa Ziehau 		tso_maxlen = IP_MAXPACKET;
497415516c77SSepherosa Ziehau 	if (tso_maxlen > sc->hn_ndis_tso_szmax)
497515516c77SSepherosa Ziehau 		tso_maxlen = sc->hn_ndis_tso_szmax;
49769c6cae24SSepherosa Ziehau 	hw_tsomax = tso_maxlen - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
49779c6cae24SSepherosa Ziehau 
49789c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_isready(sc)) {
49799c6cae24SSepherosa Ziehau 		if (hw_tsomax > sc->hn_vf_ifp->if_hw_tsomax)
49809c6cae24SSepherosa Ziehau 			hw_tsomax = sc->hn_vf_ifp->if_hw_tsomax;
49819c6cae24SSepherosa Ziehau 	}
49829c6cae24SSepherosa Ziehau 	ifp->if_hw_tsomax = hw_tsomax;
498315516c77SSepherosa Ziehau 	if (bootverbose)
498415516c77SSepherosa Ziehau 		if_printf(ifp, "TSO size max %u\n", ifp->if_hw_tsomax);
498515516c77SSepherosa Ziehau }
498615516c77SSepherosa Ziehau 
498715516c77SSepherosa Ziehau static void
498815516c77SSepherosa Ziehau hn_fixup_tx_data(struct hn_softc *sc)
498915516c77SSepherosa Ziehau {
499015516c77SSepherosa Ziehau 	uint64_t csum_assist;
499115516c77SSepherosa Ziehau 	int i;
499215516c77SSepherosa Ziehau 
499315516c77SSepherosa Ziehau 	hn_set_chim_size(sc, sc->hn_chim_szmax);
499415516c77SSepherosa Ziehau 	if (hn_tx_chimney_size > 0 &&
499515516c77SSepherosa Ziehau 	    hn_tx_chimney_size < sc->hn_chim_szmax)
499615516c77SSepherosa Ziehau 		hn_set_chim_size(sc, hn_tx_chimney_size);
499715516c77SSepherosa Ziehau 
499815516c77SSepherosa Ziehau 	csum_assist = 0;
499915516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_IPCS)
500015516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP;
500115516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP4CS)
500215516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_TCP;
500315516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_UDP4CS)
500415516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_UDP;
500515516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP6CS)
500615516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_TCP;
500715516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_UDP6CS)
500815516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_UDP;
500915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
501015516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_csum_assist = csum_assist;
501115516c77SSepherosa Ziehau 
501215516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_HASHVAL) {
501315516c77SSepherosa Ziehau 		/*
501415516c77SSepherosa Ziehau 		 * Support HASHVAL pktinfo on TX path.
501515516c77SSepherosa Ziehau 		 */
501615516c77SSepherosa Ziehau 		if (bootverbose)
501715516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "support HASHVAL pktinfo\n");
501815516c77SSepherosa Ziehau 		for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
501915516c77SSepherosa Ziehau 			sc->hn_tx_ring[i].hn_tx_flags |= HN_TX_FLAG_HASHVAL;
502015516c77SSepherosa Ziehau 	}
502115516c77SSepherosa Ziehau }
502215516c77SSepherosa Ziehau 
502315516c77SSepherosa Ziehau static void
502415516c77SSepherosa Ziehau hn_destroy_tx_data(struct hn_softc *sc)
502515516c77SSepherosa Ziehau {
502615516c77SSepherosa Ziehau 	int i;
502715516c77SSepherosa Ziehau 
502815516c77SSepherosa Ziehau 	if (sc->hn_chim != NULL) {
50292494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_CHIM_REF) == 0) {
503015516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_chim_dma, sc->hn_chim);
50312494d735SSepherosa Ziehau 		} else {
50322494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
50332494d735SSepherosa Ziehau 			    "chimney sending buffer is referenced");
50342494d735SSepherosa Ziehau 		}
503515516c77SSepherosa Ziehau 		sc->hn_chim = NULL;
503615516c77SSepherosa Ziehau 	}
503715516c77SSepherosa Ziehau 
503815516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt == 0)
503915516c77SSepherosa Ziehau 		return;
504015516c77SSepherosa Ziehau 
504115516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
504215516c77SSepherosa Ziehau 		hn_tx_ring_destroy(&sc->hn_tx_ring[i]);
504315516c77SSepherosa Ziehau 
504415516c77SSepherosa Ziehau 	free(sc->hn_tx_ring, M_DEVBUF);
504515516c77SSepherosa Ziehau 	sc->hn_tx_ring = NULL;
504615516c77SSepherosa Ziehau 
504715516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = 0;
504815516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = 0;
504915516c77SSepherosa Ziehau }
505015516c77SSepherosa Ziehau 
505123bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
505223bf9e15SSepherosa Ziehau 
505315516c77SSepherosa Ziehau static void
505415516c77SSepherosa Ziehau hn_start_taskfunc(void *xtxr, int pending __unused)
505515516c77SSepherosa Ziehau {
505615516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
505715516c77SSepherosa Ziehau 
505815516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
505915516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
506015516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
506115516c77SSepherosa Ziehau }
506215516c77SSepherosa Ziehau 
506323bf9e15SSepherosa Ziehau static int
506423bf9e15SSepherosa Ziehau hn_start_locked(struct hn_tx_ring *txr, int len)
506523bf9e15SSepherosa Ziehau {
506623bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
506723bf9e15SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
5068dc13fee6SSepherosa Ziehau 	int sched = 0;
506923bf9e15SSepherosa Ziehau 
507023bf9e15SSepherosa Ziehau 	KASSERT(hn_use_if_start,
507123bf9e15SSepherosa Ziehau 	    ("hn_start_locked is called, when if_start is disabled"));
507223bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
507323bf9e15SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
5074dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
507523bf9e15SSepherosa Ziehau 
507623bf9e15SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5077dc13fee6SSepherosa Ziehau 		return (0);
507823bf9e15SSepherosa Ziehau 
507923bf9e15SSepherosa Ziehau 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
508023bf9e15SSepherosa Ziehau 	    IFF_DRV_RUNNING)
5081dc13fee6SSepherosa Ziehau 		return (0);
508223bf9e15SSepherosa Ziehau 
508323bf9e15SSepherosa Ziehau 	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
508423bf9e15SSepherosa Ziehau 		struct hn_txdesc *txd;
508523bf9e15SSepherosa Ziehau 		struct mbuf *m_head;
508623bf9e15SSepherosa Ziehau 		int error;
508723bf9e15SSepherosa Ziehau 
508823bf9e15SSepherosa Ziehau 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
508923bf9e15SSepherosa Ziehau 		if (m_head == NULL)
509023bf9e15SSepherosa Ziehau 			break;
509123bf9e15SSepherosa Ziehau 
509223bf9e15SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
509323bf9e15SSepherosa Ziehau 			/*
509423bf9e15SSepherosa Ziehau 			 * This sending could be time consuming; let callers
509523bf9e15SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
509623bf9e15SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
509723bf9e15SSepherosa Ziehau 			 */
509823bf9e15SSepherosa Ziehau 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
5099dc13fee6SSepherosa Ziehau 			sched = 1;
5100dc13fee6SSepherosa Ziehau 			break;
510123bf9e15SSepherosa Ziehau 		}
510223bf9e15SSepherosa Ziehau 
5103edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
5104edd3f315SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
5105edd3f315SSepherosa Ziehau 			m_head = hn_tso_fixup(m_head);
5106edd3f315SSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
5107edd3f315SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5108edd3f315SSepherosa Ziehau 				continue;
5109edd3f315SSepherosa Ziehau 			}
5110edd3f315SSepherosa Ziehau 		}
5111edd3f315SSepherosa Ziehau #endif
5112edd3f315SSepherosa Ziehau 
511323bf9e15SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
511423bf9e15SSepherosa Ziehau 		if (txd == NULL) {
511523bf9e15SSepherosa Ziehau 			txr->hn_no_txdescs++;
511623bf9e15SSepherosa Ziehau 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
511723bf9e15SSepherosa Ziehau 			atomic_set_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
511823bf9e15SSepherosa Ziehau 			break;
511923bf9e15SSepherosa Ziehau 		}
512023bf9e15SSepherosa Ziehau 
5121dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
512223bf9e15SSepherosa Ziehau 		if (error) {
512323bf9e15SSepherosa Ziehau 			/* Both txd and m_head are freed */
5124dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5125dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
512623bf9e15SSepherosa Ziehau 			continue;
512723bf9e15SSepherosa Ziehau 		}
512823bf9e15SSepherosa Ziehau 
5129dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5130dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5131dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5132dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5133dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
5134dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5135dc13fee6SSepherosa Ziehau 					atomic_set_int(&ifp->if_drv_flags,
5136dc13fee6SSepherosa Ziehau 					    IFF_DRV_OACTIVE);
5137dc13fee6SSepherosa Ziehau 					break;
5138dc13fee6SSepherosa Ziehau 				}
5139dc13fee6SSepherosa Ziehau 			} else {
5140dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
514123bf9e15SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
514223bf9e15SSepherosa Ziehau 				if (__predict_false(error)) {
514323bf9e15SSepherosa Ziehau 					/* txd is freed, but m_head is not */
514423bf9e15SSepherosa Ziehau 					IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
5145dc13fee6SSepherosa Ziehau 					atomic_set_int(&ifp->if_drv_flags,
5146dc13fee6SSepherosa Ziehau 					    IFF_DRV_OACTIVE);
514723bf9e15SSepherosa Ziehau 					break;
514823bf9e15SSepherosa Ziehau 				}
514923bf9e15SSepherosa Ziehau 			}
5150dc13fee6SSepherosa Ziehau 		}
5151dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5152dc13fee6SSepherosa Ziehau 		else {
5153dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5154dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5155dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5156dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5157dc13fee6SSepherosa Ziehau 		}
5158dc13fee6SSepherosa Ziehau #endif
5159dc13fee6SSepherosa Ziehau 	}
5160dc13fee6SSepherosa Ziehau 
5161dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5162dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5163dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5164dc13fee6SSepherosa Ziehau 	return (sched);
516523bf9e15SSepherosa Ziehau }
516623bf9e15SSepherosa Ziehau 
516723bf9e15SSepherosa Ziehau static void
516823bf9e15SSepherosa Ziehau hn_start(struct ifnet *ifp)
516923bf9e15SSepherosa Ziehau {
517023bf9e15SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
517123bf9e15SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[0];
517223bf9e15SSepherosa Ziehau 
517323bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
517423bf9e15SSepherosa Ziehau 		goto do_sched;
517523bf9e15SSepherosa Ziehau 
517623bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
517723bf9e15SSepherosa Ziehau 		int sched;
517823bf9e15SSepherosa Ziehau 
517923bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
518023bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
518123bf9e15SSepherosa Ziehau 		if (!sched)
518223bf9e15SSepherosa Ziehau 			return;
518323bf9e15SSepherosa Ziehau 	}
518423bf9e15SSepherosa Ziehau do_sched:
518523bf9e15SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
518623bf9e15SSepherosa Ziehau }
518723bf9e15SSepherosa Ziehau 
518815516c77SSepherosa Ziehau static void
518915516c77SSepherosa Ziehau hn_start_txeof_taskfunc(void *xtxr, int pending __unused)
519015516c77SSepherosa Ziehau {
519115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
519215516c77SSepherosa Ziehau 
519315516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
519415516c77SSepherosa Ziehau 	atomic_clear_int(&txr->hn_sc->hn_ifp->if_drv_flags, IFF_DRV_OACTIVE);
519515516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
519615516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
519715516c77SSepherosa Ziehau }
519815516c77SSepherosa Ziehau 
519923bf9e15SSepherosa Ziehau static void
520023bf9e15SSepherosa Ziehau hn_start_txeof(struct hn_tx_ring *txr)
520123bf9e15SSepherosa Ziehau {
520223bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
520323bf9e15SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
520423bf9e15SSepherosa Ziehau 
520523bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
520623bf9e15SSepherosa Ziehau 
520723bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
520823bf9e15SSepherosa Ziehau 		goto do_sched;
520923bf9e15SSepherosa Ziehau 
521023bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
521123bf9e15SSepherosa Ziehau 		int sched;
521223bf9e15SSepherosa Ziehau 
521323bf9e15SSepherosa Ziehau 		atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
521423bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
521523bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
521623bf9e15SSepherosa Ziehau 		if (sched) {
521723bf9e15SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
521823bf9e15SSepherosa Ziehau 			    &txr->hn_tx_task);
521923bf9e15SSepherosa Ziehau 		}
522023bf9e15SSepherosa Ziehau 	} else {
522123bf9e15SSepherosa Ziehau do_sched:
522223bf9e15SSepherosa Ziehau 		/*
522323bf9e15SSepherosa Ziehau 		 * Release the OACTIVE earlier, with the hope, that
522423bf9e15SSepherosa Ziehau 		 * others could catch up.  The task will clear the
522523bf9e15SSepherosa Ziehau 		 * flag again with the hn_tx_lock to avoid possible
522623bf9e15SSepherosa Ziehau 		 * races.
522723bf9e15SSepherosa Ziehau 		 */
522823bf9e15SSepherosa Ziehau 		atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
522923bf9e15SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
523023bf9e15SSepherosa Ziehau 	}
523123bf9e15SSepherosa Ziehau }
523223bf9e15SSepherosa Ziehau 
523323bf9e15SSepherosa Ziehau #endif	/* HN_IFSTART_SUPPORT */
523423bf9e15SSepherosa Ziehau 
523515516c77SSepherosa Ziehau static int
523615516c77SSepherosa Ziehau hn_xmit(struct hn_tx_ring *txr, int len)
523715516c77SSepherosa Ziehau {
523815516c77SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
523915516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
524015516c77SSepherosa Ziehau 	struct mbuf *m_head;
5241dc13fee6SSepherosa Ziehau 	int sched = 0;
524215516c77SSepherosa Ziehau 
524315516c77SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
524423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
524515516c77SSepherosa Ziehau 	KASSERT(hn_use_if_start == 0,
524615516c77SSepherosa Ziehau 	    ("hn_xmit is called, when if_start is enabled"));
524723bf9e15SSepherosa Ziehau #endif
5248dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
524915516c77SSepherosa Ziehau 
525015516c77SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
5251dc13fee6SSepherosa Ziehau 		return (0);
525215516c77SSepherosa Ziehau 
525315516c77SSepherosa Ziehau 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || txr->hn_oactive)
5254dc13fee6SSepherosa Ziehau 		return (0);
525515516c77SSepherosa Ziehau 
525615516c77SSepherosa Ziehau 	while ((m_head = drbr_peek(ifp, txr->hn_mbuf_br)) != NULL) {
525715516c77SSepherosa Ziehau 		struct hn_txdesc *txd;
525815516c77SSepherosa Ziehau 		int error;
525915516c77SSepherosa Ziehau 
526015516c77SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
526115516c77SSepherosa Ziehau 			/*
526215516c77SSepherosa Ziehau 			 * This sending could be time consuming; let callers
526315516c77SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
526415516c77SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
526515516c77SSepherosa Ziehau 			 */
526615516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
5267dc13fee6SSepherosa Ziehau 			sched = 1;
5268dc13fee6SSepherosa Ziehau 			break;
526915516c77SSepherosa Ziehau 		}
527015516c77SSepherosa Ziehau 
527115516c77SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
527215516c77SSepherosa Ziehau 		if (txd == NULL) {
527315516c77SSepherosa Ziehau 			txr->hn_no_txdescs++;
527415516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
527515516c77SSepherosa Ziehau 			txr->hn_oactive = 1;
527615516c77SSepherosa Ziehau 			break;
527715516c77SSepherosa Ziehau 		}
527815516c77SSepherosa Ziehau 
5279dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
528015516c77SSepherosa Ziehau 		if (error) {
528115516c77SSepherosa Ziehau 			/* Both txd and m_head are freed; discard */
5282dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
5283dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
528415516c77SSepherosa Ziehau 			drbr_advance(ifp, txr->hn_mbuf_br);
528515516c77SSepherosa Ziehau 			continue;
528615516c77SSepherosa Ziehau 		}
528715516c77SSepherosa Ziehau 
5288dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
5289dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
5290dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
5291dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
5292dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
529315516c77SSepherosa Ziehau 				if (__predict_false(error)) {
529415516c77SSepherosa Ziehau 					txr->hn_oactive = 1;
529515516c77SSepherosa Ziehau 					break;
529615516c77SSepherosa Ziehau 				}
5297dc13fee6SSepherosa Ziehau 			} else {
5298dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
5299dc13fee6SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
5300dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
5301dc13fee6SSepherosa Ziehau 					/* txd is freed, but m_head is not */
5302dc13fee6SSepherosa Ziehau 					drbr_putback(ifp, txr->hn_mbuf_br,
5303dc13fee6SSepherosa Ziehau 					    m_head);
5304dc13fee6SSepherosa Ziehau 					txr->hn_oactive = 1;
5305dc13fee6SSepherosa Ziehau 					break;
5306dc13fee6SSepherosa Ziehau 				}
5307dc13fee6SSepherosa Ziehau 			}
5308dc13fee6SSepherosa Ziehau 		}
5309dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
5310dc13fee6SSepherosa Ziehau 		else {
5311dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
5312dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
5313dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
5314dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
5315dc13fee6SSepherosa Ziehau 		}
5316dc13fee6SSepherosa Ziehau #endif
531715516c77SSepherosa Ziehau 
531815516c77SSepherosa Ziehau 		/* Sent */
531915516c77SSepherosa Ziehau 		drbr_advance(ifp, txr->hn_mbuf_br);
532015516c77SSepherosa Ziehau 	}
5321dc13fee6SSepherosa Ziehau 
5322dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
5323dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
5324dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
5325dc13fee6SSepherosa Ziehau 	return (sched);
532615516c77SSepherosa Ziehau }
532715516c77SSepherosa Ziehau 
532815516c77SSepherosa Ziehau static int
532915516c77SSepherosa Ziehau hn_transmit(struct ifnet *ifp, struct mbuf *m)
533015516c77SSepherosa Ziehau {
533115516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
533215516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
533315516c77SSepherosa Ziehau 	int error, idx = 0;
533415516c77SSepherosa Ziehau 
53359c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED) {
53369c6cae24SSepherosa Ziehau 		struct rm_priotracker pt;
53379c6cae24SSepherosa Ziehau 
53389c6cae24SSepherosa Ziehau 		rm_rlock(&sc->hn_vf_lock, &pt);
53399c6cae24SSepherosa Ziehau 		if (__predict_true(sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)) {
53409c6cae24SSepherosa Ziehau 			struct mbuf *m_bpf = NULL;
53419c6cae24SSepherosa Ziehau 			int obytes, omcast;
53429c6cae24SSepherosa Ziehau 
53439c6cae24SSepherosa Ziehau 			obytes = m->m_pkthdr.len;
53449c6cae24SSepherosa Ziehau 			if (m->m_flags & M_MCAST)
53459c6cae24SSepherosa Ziehau 				omcast = 1;
53469c6cae24SSepherosa Ziehau 
53479c6cae24SSepherosa Ziehau 			if (sc->hn_xvf_flags & HN_XVFFLAG_ACCBPF) {
53489c6cae24SSepherosa Ziehau 				if (bpf_peers_present(ifp->if_bpf)) {
53499c6cae24SSepherosa Ziehau 					m_bpf = m_copypacket(m, M_NOWAIT);
53509c6cae24SSepherosa Ziehau 					if (m_bpf == NULL) {
53519c6cae24SSepherosa Ziehau 						/*
53529c6cae24SSepherosa Ziehau 						 * Failed to grab a shallow
53539c6cae24SSepherosa Ziehau 						 * copy; tap now.
53549c6cae24SSepherosa Ziehau 						 */
53559c6cae24SSepherosa Ziehau 						ETHER_BPF_MTAP(ifp, m);
53569c6cae24SSepherosa Ziehau 					}
53579c6cae24SSepherosa Ziehau 				}
53589c6cae24SSepherosa Ziehau 			} else {
53599c6cae24SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, m);
53609c6cae24SSepherosa Ziehau 			}
53619c6cae24SSepherosa Ziehau 
53629c6cae24SSepherosa Ziehau 			error = sc->hn_vf_ifp->if_transmit(sc->hn_vf_ifp, m);
53639c6cae24SSepherosa Ziehau 			rm_runlock(&sc->hn_vf_lock, &pt);
53649c6cae24SSepherosa Ziehau 
53659c6cae24SSepherosa Ziehau 			if (m_bpf != NULL) {
53669c6cae24SSepherosa Ziehau 				if (!error)
53679c6cae24SSepherosa Ziehau 					ETHER_BPF_MTAP(ifp, m_bpf);
53689c6cae24SSepherosa Ziehau 				m_freem(m_bpf);
53699c6cae24SSepherosa Ziehau 			}
53709c6cae24SSepherosa Ziehau 
53719c6cae24SSepherosa Ziehau 			if (error == ENOBUFS) {
53729c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
53739c6cae24SSepherosa Ziehau 			} else if (error) {
53749c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
53759c6cae24SSepherosa Ziehau 			} else {
53769c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
53779c6cae24SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OBYTES, obytes);
53789c6cae24SSepherosa Ziehau 				if (omcast) {
53799c6cae24SSepherosa Ziehau 					if_inc_counter(ifp, IFCOUNTER_OMCASTS,
53809c6cae24SSepherosa Ziehau 					    omcast);
53819c6cae24SSepherosa Ziehau 				}
53829c6cae24SSepherosa Ziehau 			}
53839c6cae24SSepherosa Ziehau 			return (error);
53849c6cae24SSepherosa Ziehau 		}
53859c6cae24SSepherosa Ziehau 		rm_runlock(&sc->hn_vf_lock, &pt);
53869c6cae24SSepherosa Ziehau 	}
53879c6cae24SSepherosa Ziehau 
5388edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
5389edd3f315SSepherosa Ziehau 	/*
5390edd3f315SSepherosa Ziehau 	 * Perform TSO packet header fixup now, since the TSO
5391edd3f315SSepherosa Ziehau 	 * packet header should be cache-hot.
5392edd3f315SSepherosa Ziehau 	 */
5393edd3f315SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
5394edd3f315SSepherosa Ziehau 		m = hn_tso_fixup(m);
5395edd3f315SSepherosa Ziehau 		if (__predict_false(m == NULL)) {
5396edd3f315SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
5397edd3f315SSepherosa Ziehau 			return EIO;
5398edd3f315SSepherosa Ziehau 		}
5399edd3f315SSepherosa Ziehau 	}
5400edd3f315SSepherosa Ziehau #endif
5401edd3f315SSepherosa Ziehau 
540215516c77SSepherosa Ziehau 	/*
540315516c77SSepherosa Ziehau 	 * Select the TX ring based on flowid
540415516c77SSepherosa Ziehau 	 */
540534d68912SSepherosa Ziehau 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
540634d68912SSepherosa Ziehau #ifdef RSS
540734d68912SSepherosa Ziehau 		uint32_t bid;
540834d68912SSepherosa Ziehau 
540934d68912SSepherosa Ziehau 		if (rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m),
541034d68912SSepherosa Ziehau 		    &bid) == 0)
541134d68912SSepherosa Ziehau 			idx = bid % sc->hn_tx_ring_inuse;
541234d68912SSepherosa Ziehau 		else
541334d68912SSepherosa Ziehau #endif
5414cc0c6ebcSSepherosa Ziehau 		{
5415cc0c6ebcSSepherosa Ziehau #if defined(INET6) || defined(INET)
5416cc0c6ebcSSepherosa Ziehau 			int tcpsyn = 0;
5417cc0c6ebcSSepherosa Ziehau 
5418cc0c6ebcSSepherosa Ziehau 			if (m->m_pkthdr.len < 128 &&
5419cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags &
5420cc0c6ebcSSepherosa Ziehau 			     (CSUM_IP_TCP | CSUM_IP6_TCP)) &&
5421cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
5422cc0c6ebcSSepherosa Ziehau 				m = hn_check_tcpsyn(m, &tcpsyn);
5423cc0c6ebcSSepherosa Ziehau 				if (__predict_false(m == NULL)) {
5424cc0c6ebcSSepherosa Ziehau 					if_inc_counter(ifp,
5425cc0c6ebcSSepherosa Ziehau 					    IFCOUNTER_OERRORS, 1);
5426cc0c6ebcSSepherosa Ziehau 					return (EIO);
5427cc0c6ebcSSepherosa Ziehau 				}
5428cc0c6ebcSSepherosa Ziehau 			}
5429cc0c6ebcSSepherosa Ziehau #else
5430cc0c6ebcSSepherosa Ziehau 			const int tcpsyn = 0;
5431cc0c6ebcSSepherosa Ziehau #endif
5432cc0c6ebcSSepherosa Ziehau 			if (tcpsyn)
5433cc0c6ebcSSepherosa Ziehau 				idx = 0;
5434cc0c6ebcSSepherosa Ziehau 			else
543515516c77SSepherosa Ziehau 				idx = m->m_pkthdr.flowid % sc->hn_tx_ring_inuse;
543634d68912SSepherosa Ziehau 		}
5437cc0c6ebcSSepherosa Ziehau 	}
543815516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[idx];
543915516c77SSepherosa Ziehau 
544015516c77SSepherosa Ziehau 	error = drbr_enqueue(ifp, txr->hn_mbuf_br, m);
544115516c77SSepherosa Ziehau 	if (error) {
544215516c77SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
544315516c77SSepherosa Ziehau 		return error;
544415516c77SSepherosa Ziehau 	}
544515516c77SSepherosa Ziehau 
544615516c77SSepherosa Ziehau 	if (txr->hn_oactive)
544715516c77SSepherosa Ziehau 		return 0;
544815516c77SSepherosa Ziehau 
544915516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
545015516c77SSepherosa Ziehau 		goto do_sched;
545115516c77SSepherosa Ziehau 
545215516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
545315516c77SSepherosa Ziehau 		int sched;
545415516c77SSepherosa Ziehau 
545515516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
545615516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
545715516c77SSepherosa Ziehau 		if (!sched)
545815516c77SSepherosa Ziehau 			return 0;
545915516c77SSepherosa Ziehau 	}
546015516c77SSepherosa Ziehau do_sched:
546115516c77SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
546215516c77SSepherosa Ziehau 	return 0;
546315516c77SSepherosa Ziehau }
546415516c77SSepherosa Ziehau 
546515516c77SSepherosa Ziehau static void
546615516c77SSepherosa Ziehau hn_tx_ring_qflush(struct hn_tx_ring *txr)
546715516c77SSepherosa Ziehau {
546815516c77SSepherosa Ziehau 	struct mbuf *m;
546915516c77SSepherosa Ziehau 
547015516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
547115516c77SSepherosa Ziehau 	while ((m = buf_ring_dequeue_sc(txr->hn_mbuf_br)) != NULL)
547215516c77SSepherosa Ziehau 		m_freem(m);
547315516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
547415516c77SSepherosa Ziehau }
547515516c77SSepherosa Ziehau 
547615516c77SSepherosa Ziehau static void
547715516c77SSepherosa Ziehau hn_xmit_qflush(struct ifnet *ifp)
547815516c77SSepherosa Ziehau {
547915516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
54809c6cae24SSepherosa Ziehau 	struct rm_priotracker pt;
548115516c77SSepherosa Ziehau 	int i;
548215516c77SSepherosa Ziehau 
548315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
548415516c77SSepherosa Ziehau 		hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
548515516c77SSepherosa Ziehau 	if_qflush(ifp);
54869c6cae24SSepherosa Ziehau 
54879c6cae24SSepherosa Ziehau 	rm_rlock(&sc->hn_vf_lock, &pt);
54889c6cae24SSepherosa Ziehau 	if (sc->hn_xvf_flags & HN_XVFFLAG_ENABLED)
54899c6cae24SSepherosa Ziehau 		sc->hn_vf_ifp->if_qflush(sc->hn_vf_ifp);
54909c6cae24SSepherosa Ziehau 	rm_runlock(&sc->hn_vf_lock, &pt);
549115516c77SSepherosa Ziehau }
549215516c77SSepherosa Ziehau 
549315516c77SSepherosa Ziehau static void
549415516c77SSepherosa Ziehau hn_xmit_txeof(struct hn_tx_ring *txr)
549515516c77SSepherosa Ziehau {
549615516c77SSepherosa Ziehau 
549715516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
549815516c77SSepherosa Ziehau 		goto do_sched;
549915516c77SSepherosa Ziehau 
550015516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
550115516c77SSepherosa Ziehau 		int sched;
550215516c77SSepherosa Ziehau 
550315516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
550415516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
550515516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
550615516c77SSepherosa Ziehau 		if (sched) {
550715516c77SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
550815516c77SSepherosa Ziehau 			    &txr->hn_tx_task);
550915516c77SSepherosa Ziehau 		}
551015516c77SSepherosa Ziehau 	} else {
551115516c77SSepherosa Ziehau do_sched:
551215516c77SSepherosa Ziehau 		/*
551315516c77SSepherosa Ziehau 		 * Release the oactive earlier, with the hope, that
551415516c77SSepherosa Ziehau 		 * others could catch up.  The task will clear the
551515516c77SSepherosa Ziehau 		 * oactive again with the hn_tx_lock to avoid possible
551615516c77SSepherosa Ziehau 		 * races.
551715516c77SSepherosa Ziehau 		 */
551815516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
551915516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
552015516c77SSepherosa Ziehau 	}
552115516c77SSepherosa Ziehau }
552215516c77SSepherosa Ziehau 
552315516c77SSepherosa Ziehau static void
552415516c77SSepherosa Ziehau hn_xmit_taskfunc(void *xtxr, int pending __unused)
552515516c77SSepherosa Ziehau {
552615516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
552715516c77SSepherosa Ziehau 
552815516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
552915516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
553015516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
553115516c77SSepherosa Ziehau }
553215516c77SSepherosa Ziehau 
553315516c77SSepherosa Ziehau static void
553415516c77SSepherosa Ziehau hn_xmit_txeof_taskfunc(void *xtxr, int pending __unused)
553515516c77SSepherosa Ziehau {
553615516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
553715516c77SSepherosa Ziehau 
553815516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
553915516c77SSepherosa Ziehau 	txr->hn_oactive = 0;
554015516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
554115516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
554215516c77SSepherosa Ziehau }
554315516c77SSepherosa Ziehau 
554415516c77SSepherosa Ziehau static int
554515516c77SSepherosa Ziehau hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
554615516c77SSepherosa Ziehau {
554715516c77SSepherosa Ziehau 	struct vmbus_chan_br cbr;
554815516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
554915516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = NULL;
555015516c77SSepherosa Ziehau 	int idx, error;
555115516c77SSepherosa Ziehau 
555215516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
555315516c77SSepherosa Ziehau 
555415516c77SSepherosa Ziehau 	/*
555515516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
555615516c77SSepherosa Ziehau 	 */
555715516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
555815516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
555915516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
556015516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
556115516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED) == 0,
556215516c77SSepherosa Ziehau 	    ("RX ring %d already attached", idx));
556315516c77SSepherosa Ziehau 	rxr->hn_rx_flags |= HN_RX_FLAG_ATTACHED;
55643ab0fea1SDexuan Cui 	rxr->hn_chan = chan;
556515516c77SSepherosa Ziehau 
556615516c77SSepherosa Ziehau 	if (bootverbose) {
556715516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "link RX ring %d to chan%u\n",
556815516c77SSepherosa Ziehau 		    idx, vmbus_chan_id(chan));
556915516c77SSepherosa Ziehau 	}
557015516c77SSepherosa Ziehau 
557115516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
557215516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[idx];
557315516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED) == 0,
557415516c77SSepherosa Ziehau 		    ("TX ring %d already attached", idx));
557515516c77SSepherosa Ziehau 		txr->hn_tx_flags |= HN_TX_FLAG_ATTACHED;
557615516c77SSepherosa Ziehau 
557715516c77SSepherosa Ziehau 		txr->hn_chan = chan;
557815516c77SSepherosa Ziehau 		if (bootverbose) {
557915516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "link TX ring %d to chan%u\n",
558015516c77SSepherosa Ziehau 			    idx, vmbus_chan_id(chan));
558115516c77SSepherosa Ziehau 		}
558215516c77SSepherosa Ziehau 	}
558315516c77SSepherosa Ziehau 
558415516c77SSepherosa Ziehau 	/* Bind this channel to a proper CPU. */
55850e11868dSSepherosa Ziehau 	vmbus_chan_cpu_set(chan, HN_RING_IDX2CPU(sc, idx));
558615516c77SSepherosa Ziehau 
558715516c77SSepherosa Ziehau 	/*
558815516c77SSepherosa Ziehau 	 * Open this channel
558915516c77SSepherosa Ziehau 	 */
559015516c77SSepherosa Ziehau 	cbr.cbr = rxr->hn_br;
559115516c77SSepherosa Ziehau 	cbr.cbr_paddr = rxr->hn_br_dma.hv_paddr;
559215516c77SSepherosa Ziehau 	cbr.cbr_txsz = HN_TXBR_SIZE;
559315516c77SSepherosa Ziehau 	cbr.cbr_rxsz = HN_RXBR_SIZE;
559415516c77SSepherosa Ziehau 	error = vmbus_chan_open_br(chan, &cbr, NULL, 0, hn_chan_callback, rxr);
559515516c77SSepherosa Ziehau 	if (error) {
559671e8ac56SSepherosa Ziehau 		if (error == EISCONN) {
559771e8ac56SSepherosa Ziehau 			if_printf(sc->hn_ifp, "bufring is connected after "
559871e8ac56SSepherosa Ziehau 			    "chan%u open failure\n", vmbus_chan_id(chan));
559971e8ac56SSepherosa Ziehau 			rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
560071e8ac56SSepherosa Ziehau 		} else {
560115516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "open chan%u failed: %d\n",
560215516c77SSepherosa Ziehau 			    vmbus_chan_id(chan), error);
560371e8ac56SSepherosa Ziehau 		}
560415516c77SSepherosa Ziehau 	}
560515516c77SSepherosa Ziehau 	return (error);
560615516c77SSepherosa Ziehau }
560715516c77SSepherosa Ziehau 
560815516c77SSepherosa Ziehau static void
560915516c77SSepherosa Ziehau hn_chan_detach(struct hn_softc *sc, struct vmbus_channel *chan)
561015516c77SSepherosa Ziehau {
561115516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
56122494d735SSepherosa Ziehau 	int idx, error;
561315516c77SSepherosa Ziehau 
561415516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
561515516c77SSepherosa Ziehau 
561615516c77SSepherosa Ziehau 	/*
561715516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
561815516c77SSepherosa Ziehau 	 */
561915516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
562015516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
562115516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
562215516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
562315516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED),
562415516c77SSepherosa Ziehau 	    ("RX ring %d is not attached", idx));
562515516c77SSepherosa Ziehau 	rxr->hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
562615516c77SSepherosa Ziehau 
562715516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
562815516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[idx];
562915516c77SSepherosa Ziehau 
563015516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED),
563115516c77SSepherosa Ziehau 		    ("TX ring %d is not attached attached", idx));
563215516c77SSepherosa Ziehau 		txr->hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
563315516c77SSepherosa Ziehau 	}
563415516c77SSepherosa Ziehau 
563515516c77SSepherosa Ziehau 	/*
563615516c77SSepherosa Ziehau 	 * Close this channel.
563715516c77SSepherosa Ziehau 	 *
563815516c77SSepherosa Ziehau 	 * NOTE:
563915516c77SSepherosa Ziehau 	 * Channel closing does _not_ destroy the target channel.
564015516c77SSepherosa Ziehau 	 */
56412494d735SSepherosa Ziehau 	error = vmbus_chan_close_direct(chan);
56422494d735SSepherosa Ziehau 	if (error == EISCONN) {
5643aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u bufring is connected "
5644aa1a2adcSSepherosa Ziehau 		    "after being closed\n", vmbus_chan_id(chan));
56452494d735SSepherosa Ziehau 		rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
56462494d735SSepherosa Ziehau 	} else if (error) {
5647aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u close failed: %d\n",
5648aa1a2adcSSepherosa Ziehau 		    vmbus_chan_id(chan), error);
56492494d735SSepherosa Ziehau 	}
565015516c77SSepherosa Ziehau }
565115516c77SSepherosa Ziehau 
565215516c77SSepherosa Ziehau static int
565315516c77SSepherosa Ziehau hn_attach_subchans(struct hn_softc *sc)
565415516c77SSepherosa Ziehau {
565515516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
565615516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
565715516c77SSepherosa Ziehau 	int i, error = 0;
565815516c77SSepherosa Ziehau 
565971e8ac56SSepherosa Ziehau 	KASSERT(subchan_cnt > 0, ("no sub-channels"));
566015516c77SSepherosa Ziehau 
566115516c77SSepherosa Ziehau 	/* Attach the sub-channels. */
566215516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
566315516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i) {
566471e8ac56SSepherosa Ziehau 		int error1;
566571e8ac56SSepherosa Ziehau 
566671e8ac56SSepherosa Ziehau 		error1 = hn_chan_attach(sc, subchans[i]);
566771e8ac56SSepherosa Ziehau 		if (error1) {
566871e8ac56SSepherosa Ziehau 			error = error1;
566971e8ac56SSepherosa Ziehau 			/* Move on; all channels will be detached later. */
567071e8ac56SSepherosa Ziehau 		}
567115516c77SSepherosa Ziehau 	}
567215516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
567315516c77SSepherosa Ziehau 
567415516c77SSepherosa Ziehau 	if (error) {
567515516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "sub-channels attach failed: %d\n", error);
567615516c77SSepherosa Ziehau 	} else {
567715516c77SSepherosa Ziehau 		if (bootverbose) {
567815516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "%d sub-channels attached\n",
567915516c77SSepherosa Ziehau 			    subchan_cnt);
568015516c77SSepherosa Ziehau 		}
568115516c77SSepherosa Ziehau 	}
568215516c77SSepherosa Ziehau 	return (error);
568315516c77SSepherosa Ziehau }
568415516c77SSepherosa Ziehau 
568515516c77SSepherosa Ziehau static void
568615516c77SSepherosa Ziehau hn_detach_allchans(struct hn_softc *sc)
568715516c77SSepherosa Ziehau {
568815516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
568915516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
569015516c77SSepherosa Ziehau 	int i;
569115516c77SSepherosa Ziehau 
569215516c77SSepherosa Ziehau 	if (subchan_cnt == 0)
569315516c77SSepherosa Ziehau 		goto back;
569415516c77SSepherosa Ziehau 
569515516c77SSepherosa Ziehau 	/* Detach the sub-channels. */
569615516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
569715516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i)
569815516c77SSepherosa Ziehau 		hn_chan_detach(sc, subchans[i]);
569915516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
570015516c77SSepherosa Ziehau 
570115516c77SSepherosa Ziehau back:
570215516c77SSepherosa Ziehau 	/*
570315516c77SSepherosa Ziehau 	 * Detach the primary channel, _after_ all sub-channels
570415516c77SSepherosa Ziehau 	 * are detached.
570515516c77SSepherosa Ziehau 	 */
570615516c77SSepherosa Ziehau 	hn_chan_detach(sc, sc->hn_prichan);
570715516c77SSepherosa Ziehau 
570815516c77SSepherosa Ziehau 	/* Wait for sub-channels to be destroyed, if any. */
570915516c77SSepherosa Ziehau 	vmbus_subchan_drain(sc->hn_prichan);
571015516c77SSepherosa Ziehau 
571115516c77SSepherosa Ziehau #ifdef INVARIANTS
571215516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
571315516c77SSepherosa Ziehau 		KASSERT((sc->hn_rx_ring[i].hn_rx_flags &
571415516c77SSepherosa Ziehau 		    HN_RX_FLAG_ATTACHED) == 0,
571515516c77SSepherosa Ziehau 		    ("%dth RX ring is still attached", i));
571615516c77SSepherosa Ziehau 	}
571715516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
571815516c77SSepherosa Ziehau 		KASSERT((sc->hn_tx_ring[i].hn_tx_flags &
571915516c77SSepherosa Ziehau 		    HN_TX_FLAG_ATTACHED) == 0,
572015516c77SSepherosa Ziehau 		    ("%dth TX ring is still attached", i));
572115516c77SSepherosa Ziehau 	}
572215516c77SSepherosa Ziehau #endif
572315516c77SSepherosa Ziehau }
572415516c77SSepherosa Ziehau 
572515516c77SSepherosa Ziehau static int
572615516c77SSepherosa Ziehau hn_synth_alloc_subchans(struct hn_softc *sc, int *nsubch)
572715516c77SSepherosa Ziehau {
572815516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
572915516c77SSepherosa Ziehau 	int nchan, rxr_cnt, error;
573015516c77SSepherosa Ziehau 
573115516c77SSepherosa Ziehau 	nchan = *nsubch + 1;
573215516c77SSepherosa Ziehau 	if (nchan == 1) {
573315516c77SSepherosa Ziehau 		/*
573415516c77SSepherosa Ziehau 		 * Multiple RX/TX rings are not requested.
573515516c77SSepherosa Ziehau 		 */
573615516c77SSepherosa Ziehau 		*nsubch = 0;
573715516c77SSepherosa Ziehau 		return (0);
573815516c77SSepherosa Ziehau 	}
573915516c77SSepherosa Ziehau 
574015516c77SSepherosa Ziehau 	/*
574115516c77SSepherosa Ziehau 	 * Query RSS capabilities, e.g. # of RX rings, and # of indirect
574215516c77SSepherosa Ziehau 	 * table entries.
574315516c77SSepherosa Ziehau 	 */
574415516c77SSepherosa Ziehau 	error = hn_rndis_query_rsscaps(sc, &rxr_cnt);
574515516c77SSepherosa Ziehau 	if (error) {
574615516c77SSepherosa Ziehau 		/* No RSS; this is benign. */
574715516c77SSepherosa Ziehau 		*nsubch = 0;
574815516c77SSepherosa Ziehau 		return (0);
574915516c77SSepherosa Ziehau 	}
575015516c77SSepherosa Ziehau 	if (bootverbose) {
575115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
575215516c77SSepherosa Ziehau 		    rxr_cnt, nchan);
575315516c77SSepherosa Ziehau 	}
575415516c77SSepherosa Ziehau 
575515516c77SSepherosa Ziehau 	if (nchan > rxr_cnt)
575615516c77SSepherosa Ziehau 		nchan = rxr_cnt;
575715516c77SSepherosa Ziehau 	if (nchan == 1) {
575815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "only 1 channel is supported, no vRSS\n");
575915516c77SSepherosa Ziehau 		*nsubch = 0;
576015516c77SSepherosa Ziehau 		return (0);
576115516c77SSepherosa Ziehau 	}
576215516c77SSepherosa Ziehau 
576315516c77SSepherosa Ziehau 	/*
576415516c77SSepherosa Ziehau 	 * Allocate sub-channels from NVS.
576515516c77SSepherosa Ziehau 	 */
576615516c77SSepherosa Ziehau 	*nsubch = nchan - 1;
576715516c77SSepherosa Ziehau 	error = hn_nvs_alloc_subchans(sc, nsubch);
576815516c77SSepherosa Ziehau 	if (error || *nsubch == 0) {
576915516c77SSepherosa Ziehau 		/* Failed to allocate sub-channels. */
577015516c77SSepherosa Ziehau 		*nsubch = 0;
577115516c77SSepherosa Ziehau 		return (0);
577215516c77SSepherosa Ziehau 	}
577315516c77SSepherosa Ziehau 
577415516c77SSepherosa Ziehau 	/*
577515516c77SSepherosa Ziehau 	 * Wait for all sub-channels to become ready before moving on.
577615516c77SSepherosa Ziehau 	 */
577715516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, *nsubch);
577815516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, *nsubch);
577915516c77SSepherosa Ziehau 	return (0);
578015516c77SSepherosa Ziehau }
578115516c77SSepherosa Ziehau 
57822494d735SSepherosa Ziehau static bool
57832494d735SSepherosa Ziehau hn_synth_attachable(const struct hn_softc *sc)
57842494d735SSepherosa Ziehau {
57852494d735SSepherosa Ziehau 	int i;
57862494d735SSepherosa Ziehau 
57872494d735SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_ERRORS)
57882494d735SSepherosa Ziehau 		return (false);
57892494d735SSepherosa Ziehau 
57902494d735SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
57912494d735SSepherosa Ziehau 		const struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
57922494d735SSepherosa Ziehau 
57932494d735SSepherosa Ziehau 		if (rxr->hn_rx_flags & HN_RX_FLAG_BR_REF)
57942494d735SSepherosa Ziehau 			return (false);
57952494d735SSepherosa Ziehau 	}
57962494d735SSepherosa Ziehau 	return (true);
57972494d735SSepherosa Ziehau }
57982494d735SSepherosa Ziehau 
5799b3b75d9cSSepherosa Ziehau /*
5800b3b75d9cSSepherosa Ziehau  * Make sure that the RX filter is zero after the successful
5801b3b75d9cSSepherosa Ziehau  * RNDIS initialization.
5802b3b75d9cSSepherosa Ziehau  *
5803b3b75d9cSSepherosa Ziehau  * NOTE:
5804b3b75d9cSSepherosa Ziehau  * Under certain conditions on certain versions of Hyper-V,
5805b3b75d9cSSepherosa Ziehau  * the RNDIS rxfilter is _not_ zero on the hypervisor side
5806b3b75d9cSSepherosa Ziehau  * after the successful RNDIS initialization, which breaks
5807b3b75d9cSSepherosa Ziehau  * the assumption of any following code (well, it breaks the
5808b3b75d9cSSepherosa Ziehau  * RNDIS API contract actually).  Clear the RNDIS rxfilter
5809b3b75d9cSSepherosa Ziehau  * explicitly, drain packets sneaking through, and drain the
5810b3b75d9cSSepherosa Ziehau  * interrupt taskqueues scheduled due to the stealth packets.
5811b3b75d9cSSepherosa Ziehau  */
5812b3b75d9cSSepherosa Ziehau static void
5813b3b75d9cSSepherosa Ziehau hn_rndis_init_fixat(struct hn_softc *sc, int nchan)
5814b3b75d9cSSepherosa Ziehau {
5815b3b75d9cSSepherosa Ziehau 
5816b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
5817b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, nchan);
5818b3b75d9cSSepherosa Ziehau }
5819b3b75d9cSSepherosa Ziehau 
582015516c77SSepherosa Ziehau static int
582115516c77SSepherosa Ziehau hn_synth_attach(struct hn_softc *sc, int mtu)
582215516c77SSepherosa Ziehau {
582371e8ac56SSepherosa Ziehau #define ATTACHED_NVS		0x0002
582471e8ac56SSepherosa Ziehau #define ATTACHED_RNDIS		0x0004
582571e8ac56SSepherosa Ziehau 
582615516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
5827b3b75d9cSSepherosa Ziehau 	int error, nsubch, nchan = 1, i, rndis_inited;
582871e8ac56SSepherosa Ziehau 	uint32_t old_caps, attached = 0;
582915516c77SSepherosa Ziehau 
583015516c77SSepherosa Ziehau 	KASSERT((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0,
583115516c77SSepherosa Ziehau 	    ("synthetic parts were attached"));
583215516c77SSepherosa Ziehau 
58332494d735SSepherosa Ziehau 	if (!hn_synth_attachable(sc))
58342494d735SSepherosa Ziehau 		return (ENXIO);
58352494d735SSepherosa Ziehau 
583615516c77SSepherosa Ziehau 	/* Save capabilities for later verification. */
583715516c77SSepherosa Ziehau 	old_caps = sc->hn_caps;
583815516c77SSepherosa Ziehau 	sc->hn_caps = 0;
583915516c77SSepherosa Ziehau 
584015516c77SSepherosa Ziehau 	/* Clear RSS stuffs. */
584115516c77SSepherosa Ziehau 	sc->hn_rss_ind_size = 0;
584215516c77SSepherosa Ziehau 	sc->hn_rss_hash = 0;
584315516c77SSepherosa Ziehau 
584415516c77SSepherosa Ziehau 	/*
584515516c77SSepherosa Ziehau 	 * Attach the primary channel _before_ attaching NVS and RNDIS.
584615516c77SSepherosa Ziehau 	 */
584715516c77SSepherosa Ziehau 	error = hn_chan_attach(sc, sc->hn_prichan);
584815516c77SSepherosa Ziehau 	if (error)
584971e8ac56SSepherosa Ziehau 		goto failed;
585015516c77SSepherosa Ziehau 
585115516c77SSepherosa Ziehau 	/*
585215516c77SSepherosa Ziehau 	 * Attach NVS.
585315516c77SSepherosa Ziehau 	 */
585415516c77SSepherosa Ziehau 	error = hn_nvs_attach(sc, mtu);
585515516c77SSepherosa Ziehau 	if (error)
585671e8ac56SSepherosa Ziehau 		goto failed;
585771e8ac56SSepherosa Ziehau 	attached |= ATTACHED_NVS;
585815516c77SSepherosa Ziehau 
585915516c77SSepherosa Ziehau 	/*
586015516c77SSepherosa Ziehau 	 * Attach RNDIS _after_ NVS is attached.
586115516c77SSepherosa Ziehau 	 */
5862b3b75d9cSSepherosa Ziehau 	error = hn_rndis_attach(sc, mtu, &rndis_inited);
5863b3b75d9cSSepherosa Ziehau 	if (rndis_inited)
5864b3b75d9cSSepherosa Ziehau 		attached |= ATTACHED_RNDIS;
586515516c77SSepherosa Ziehau 	if (error)
586671e8ac56SSepherosa Ziehau 		goto failed;
586715516c77SSepherosa Ziehau 
586815516c77SSepherosa Ziehau 	/*
586915516c77SSepherosa Ziehau 	 * Make sure capabilities are not changed.
587015516c77SSepherosa Ziehau 	 */
587115516c77SSepherosa Ziehau 	if (device_is_attached(sc->hn_dev) && old_caps != sc->hn_caps) {
587215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "caps mismatch old 0x%08x, new 0x%08x\n",
587315516c77SSepherosa Ziehau 		    old_caps, sc->hn_caps);
587471e8ac56SSepherosa Ziehau 		error = ENXIO;
587571e8ac56SSepherosa Ziehau 		goto failed;
587615516c77SSepherosa Ziehau 	}
587715516c77SSepherosa Ziehau 
587815516c77SSepherosa Ziehau 	/*
587915516c77SSepherosa Ziehau 	 * Allocate sub-channels for multi-TX/RX rings.
588015516c77SSepherosa Ziehau 	 *
588115516c77SSepherosa Ziehau 	 * NOTE:
588215516c77SSepherosa Ziehau 	 * The # of RX rings that can be used is equivalent to the # of
588315516c77SSepherosa Ziehau 	 * channels to be requested.
588415516c77SSepherosa Ziehau 	 */
588515516c77SSepherosa Ziehau 	nsubch = sc->hn_rx_ring_cnt - 1;
588615516c77SSepherosa Ziehau 	error = hn_synth_alloc_subchans(sc, &nsubch);
588715516c77SSepherosa Ziehau 	if (error)
588871e8ac56SSepherosa Ziehau 		goto failed;
588971e8ac56SSepherosa Ziehau 	/* NOTE: _Full_ synthetic parts detach is required now. */
589071e8ac56SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_SYNTH_ATTACHED;
589115516c77SSepherosa Ziehau 
589271e8ac56SSepherosa Ziehau 	/*
589371e8ac56SSepherosa Ziehau 	 * Set the # of TX/RX rings that could be used according to
589471e8ac56SSepherosa Ziehau 	 * the # of channels that NVS offered.
589571e8ac56SSepherosa Ziehau 	 */
589615516c77SSepherosa Ziehau 	nchan = nsubch + 1;
589771e8ac56SSepherosa Ziehau 	hn_set_ring_inuse(sc, nchan);
589815516c77SSepherosa Ziehau 	if (nchan == 1) {
589915516c77SSepherosa Ziehau 		/* Only the primary channel can be used; done */
590015516c77SSepherosa Ziehau 		goto back;
590115516c77SSepherosa Ziehau 	}
590215516c77SSepherosa Ziehau 
590315516c77SSepherosa Ziehau 	/*
590471e8ac56SSepherosa Ziehau 	 * Attach the sub-channels.
5905afd4971bSSepherosa Ziehau 	 *
5906afd4971bSSepherosa Ziehau 	 * NOTE: hn_set_ring_inuse() _must_ have been called.
590715516c77SSepherosa Ziehau 	 */
590871e8ac56SSepherosa Ziehau 	error = hn_attach_subchans(sc);
590971e8ac56SSepherosa Ziehau 	if (error)
591071e8ac56SSepherosa Ziehau 		goto failed;
591115516c77SSepherosa Ziehau 
591271e8ac56SSepherosa Ziehau 	/*
591371e8ac56SSepherosa Ziehau 	 * Configure RSS key and indirect table _after_ all sub-channels
591471e8ac56SSepherosa Ziehau 	 * are attached.
591571e8ac56SSepherosa Ziehau 	 */
591615516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSKEY) == 0) {
591715516c77SSepherosa Ziehau 		/*
591815516c77SSepherosa Ziehau 		 * RSS key is not set yet; set it to the default RSS key.
591915516c77SSepherosa Ziehau 		 */
592015516c77SSepherosa Ziehau 		if (bootverbose)
592115516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS key\n");
592234d68912SSepherosa Ziehau #ifdef RSS
592334d68912SSepherosa Ziehau 		rss_getkey(rss->rss_key);
592434d68912SSepherosa Ziehau #else
592515516c77SSepherosa Ziehau 		memcpy(rss->rss_key, hn_rss_key_default, sizeof(rss->rss_key));
592634d68912SSepherosa Ziehau #endif
592715516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
592815516c77SSepherosa Ziehau 	}
592915516c77SSepherosa Ziehau 
593015516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSIND) == 0) {
593115516c77SSepherosa Ziehau 		/*
593215516c77SSepherosa Ziehau 		 * RSS indirect table is not set yet; set it up in round-
593315516c77SSepherosa Ziehau 		 * robin fashion.
593415516c77SSepherosa Ziehau 		 */
593515516c77SSepherosa Ziehau 		if (bootverbose) {
593615516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS indirect "
593715516c77SSepherosa Ziehau 			    "table\n");
593815516c77SSepherosa Ziehau 		}
593934d68912SSepherosa Ziehau 		for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
594034d68912SSepherosa Ziehau 			uint32_t subidx;
594134d68912SSepherosa Ziehau 
594234d68912SSepherosa Ziehau #ifdef RSS
594334d68912SSepherosa Ziehau 			subidx = rss_get_indirection_to_bucket(i);
594434d68912SSepherosa Ziehau #else
594534d68912SSepherosa Ziehau 			subidx = i;
594634d68912SSepherosa Ziehau #endif
594734d68912SSepherosa Ziehau 			rss->rss_ind[i] = subidx % nchan;
594834d68912SSepherosa Ziehau 		}
594915516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSIND;
595015516c77SSepherosa Ziehau 	} else {
595115516c77SSepherosa Ziehau 		/*
595215516c77SSepherosa Ziehau 		 * # of usable channels may be changed, so we have to
595315516c77SSepherosa Ziehau 		 * make sure that all entries in RSS indirect table
595415516c77SSepherosa Ziehau 		 * are valid.
5955afd4971bSSepherosa Ziehau 		 *
5956afd4971bSSepherosa Ziehau 		 * NOTE: hn_set_ring_inuse() _must_ have been called.
595715516c77SSepherosa Ziehau 		 */
5958afd4971bSSepherosa Ziehau 		hn_rss_ind_fixup(sc);
595915516c77SSepherosa Ziehau 	}
596015516c77SSepherosa Ziehau 
596115516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
596215516c77SSepherosa Ziehau 	if (error)
596371e8ac56SSepherosa Ziehau 		goto failed;
596471e8ac56SSepherosa Ziehau back:
5965dc13fee6SSepherosa Ziehau 	/*
5966dc13fee6SSepherosa Ziehau 	 * Fixup transmission aggregation setup.
5967dc13fee6SSepherosa Ziehau 	 */
5968dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
5969b3b75d9cSSepherosa Ziehau 	hn_rndis_init_fixat(sc, nchan);
597015516c77SSepherosa Ziehau 	return (0);
597171e8ac56SSepherosa Ziehau 
597271e8ac56SSepherosa Ziehau failed:
597371e8ac56SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
5974b3b75d9cSSepherosa Ziehau 		hn_rndis_init_fixat(sc, nchan);
597571e8ac56SSepherosa Ziehau 		hn_synth_detach(sc);
597671e8ac56SSepherosa Ziehau 	} else {
5977b3b75d9cSSepherosa Ziehau 		if (attached & ATTACHED_RNDIS) {
5978b3b75d9cSSepherosa Ziehau 			hn_rndis_init_fixat(sc, nchan);
597971e8ac56SSepherosa Ziehau 			hn_rndis_detach(sc);
5980b3b75d9cSSepherosa Ziehau 		}
598171e8ac56SSepherosa Ziehau 		if (attached & ATTACHED_NVS)
598271e8ac56SSepherosa Ziehau 			hn_nvs_detach(sc);
598371e8ac56SSepherosa Ziehau 		hn_chan_detach(sc, sc->hn_prichan);
598471e8ac56SSepherosa Ziehau 		/* Restore old capabilities. */
598571e8ac56SSepherosa Ziehau 		sc->hn_caps = old_caps;
598671e8ac56SSepherosa Ziehau 	}
598771e8ac56SSepherosa Ziehau 	return (error);
598871e8ac56SSepherosa Ziehau 
598971e8ac56SSepherosa Ziehau #undef ATTACHED_RNDIS
599071e8ac56SSepherosa Ziehau #undef ATTACHED_NVS
599115516c77SSepherosa Ziehau }
599215516c77SSepherosa Ziehau 
599315516c77SSepherosa Ziehau /*
599415516c77SSepherosa Ziehau  * NOTE:
599515516c77SSepherosa Ziehau  * The interface must have been suspended though hn_suspend(), before
599615516c77SSepherosa Ziehau  * this function get called.
599715516c77SSepherosa Ziehau  */
599815516c77SSepherosa Ziehau static void
599915516c77SSepherosa Ziehau hn_synth_detach(struct hn_softc *sc)
600015516c77SSepherosa Ziehau {
600115516c77SSepherosa Ziehau 
600215516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
600315516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
600415516c77SSepherosa Ziehau 
600515516c77SSepherosa Ziehau 	/* Detach the RNDIS first. */
600615516c77SSepherosa Ziehau 	hn_rndis_detach(sc);
600715516c77SSepherosa Ziehau 
600815516c77SSepherosa Ziehau 	/* Detach NVS. */
600915516c77SSepherosa Ziehau 	hn_nvs_detach(sc);
601015516c77SSepherosa Ziehau 
601115516c77SSepherosa Ziehau 	/* Detach all of the channels. */
601215516c77SSepherosa Ziehau 	hn_detach_allchans(sc);
601315516c77SSepherosa Ziehau 
601415516c77SSepherosa Ziehau 	sc->hn_flags &= ~HN_FLAG_SYNTH_ATTACHED;
601515516c77SSepherosa Ziehau }
601615516c77SSepherosa Ziehau 
601715516c77SSepherosa Ziehau static void
601815516c77SSepherosa Ziehau hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt)
601915516c77SSepherosa Ziehau {
602015516c77SSepherosa Ziehau 	KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_cnt,
602115516c77SSepherosa Ziehau 	    ("invalid ring count %d", ring_cnt));
602215516c77SSepherosa Ziehau 
602315516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt > ring_cnt)
602415516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = ring_cnt;
602515516c77SSepherosa Ziehau 	else
602615516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
602715516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = ring_cnt;
602815516c77SSepherosa Ziehau 
602934d68912SSepherosa Ziehau #ifdef RSS
603034d68912SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse != rss_getnumbuckets()) {
603134d68912SSepherosa Ziehau 		if_printf(sc->hn_ifp, "# of RX rings (%d) does not match "
603234d68912SSepherosa Ziehau 		    "# of RSS buckets (%d)\n", sc->hn_rx_ring_inuse,
603334d68912SSepherosa Ziehau 		    rss_getnumbuckets());
603434d68912SSepherosa Ziehau 	}
603534d68912SSepherosa Ziehau #endif
603634d68912SSepherosa Ziehau 
603715516c77SSepherosa Ziehau 	if (bootverbose) {
603815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n",
603915516c77SSepherosa Ziehau 		    sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
604015516c77SSepherosa Ziehau 	}
604115516c77SSepherosa Ziehau }
604215516c77SSepherosa Ziehau 
604315516c77SSepherosa Ziehau static void
604425641fc7SSepherosa Ziehau hn_chan_drain(struct hn_softc *sc, struct vmbus_channel *chan)
604515516c77SSepherosa Ziehau {
604615516c77SSepherosa Ziehau 
604725641fc7SSepherosa Ziehau 	/*
604825641fc7SSepherosa Ziehau 	 * NOTE:
604925641fc7SSepherosa Ziehau 	 * The TX bufring will not be drained by the hypervisor,
605025641fc7SSepherosa Ziehau 	 * if the primary channel is revoked.
605125641fc7SSepherosa Ziehau 	 */
605225641fc7SSepherosa Ziehau 	while (!vmbus_chan_rx_empty(chan) ||
605325641fc7SSepherosa Ziehau 	    (!vmbus_chan_is_revoked(sc->hn_prichan) &&
605425641fc7SSepherosa Ziehau 	     !vmbus_chan_tx_empty(chan)))
605515516c77SSepherosa Ziehau 		pause("waitch", 1);
605615516c77SSepherosa Ziehau 	vmbus_chan_intr_drain(chan);
605715516c77SSepherosa Ziehau }
605815516c77SSepherosa Ziehau 
605915516c77SSepherosa Ziehau static void
6060b3b75d9cSSepherosa Ziehau hn_disable_rx(struct hn_softc *sc)
6061b3b75d9cSSepherosa Ziehau {
6062b3b75d9cSSepherosa Ziehau 
6063b3b75d9cSSepherosa Ziehau 	/*
6064b3b75d9cSSepherosa Ziehau 	 * Disable RX by clearing RX filter forcefully.
6065b3b75d9cSSepherosa Ziehau 	 */
6066b3b75d9cSSepherosa Ziehau 	sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE;
6067b3b75d9cSSepherosa Ziehau 	hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); /* ignore error */
6068b3b75d9cSSepherosa Ziehau 
6069b3b75d9cSSepherosa Ziehau 	/*
6070b3b75d9cSSepherosa Ziehau 	 * Give RNDIS enough time to flush all pending data packets.
6071b3b75d9cSSepherosa Ziehau 	 */
6072b3b75d9cSSepherosa Ziehau 	pause("waitrx", (200 * hz) / 1000);
6073b3b75d9cSSepherosa Ziehau }
6074b3b75d9cSSepherosa Ziehau 
6075b3b75d9cSSepherosa Ziehau /*
6076b3b75d9cSSepherosa Ziehau  * NOTE:
6077b3b75d9cSSepherosa Ziehau  * RX/TX _must_ have been suspended/disabled, before this function
6078b3b75d9cSSepherosa Ziehau  * is called.
6079b3b75d9cSSepherosa Ziehau  */
6080b3b75d9cSSepherosa Ziehau static void
6081b3b75d9cSSepherosa Ziehau hn_drain_rxtx(struct hn_softc *sc, int nchan)
608215516c77SSepherosa Ziehau {
608315516c77SSepherosa Ziehau 	struct vmbus_channel **subch = NULL;
6084b3b75d9cSSepherosa Ziehau 	int nsubch;
6085b3b75d9cSSepherosa Ziehau 
6086b3b75d9cSSepherosa Ziehau 	/*
6087b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX bufrings and interrupts.
6088b3b75d9cSSepherosa Ziehau 	 */
6089b3b75d9cSSepherosa Ziehau 	nsubch = nchan - 1;
6090b3b75d9cSSepherosa Ziehau 	if (nsubch > 0)
6091b3b75d9cSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
6092b3b75d9cSSepherosa Ziehau 
6093b3b75d9cSSepherosa Ziehau 	if (subch != NULL) {
6094b3b75d9cSSepherosa Ziehau 		int i;
6095b3b75d9cSSepherosa Ziehau 
6096b3b75d9cSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
6097b3b75d9cSSepherosa Ziehau 			hn_chan_drain(sc, subch[i]);
6098b3b75d9cSSepherosa Ziehau 	}
6099b3b75d9cSSepherosa Ziehau 	hn_chan_drain(sc, sc->hn_prichan);
6100b3b75d9cSSepherosa Ziehau 
6101b3b75d9cSSepherosa Ziehau 	if (subch != NULL)
6102b3b75d9cSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
6103b3b75d9cSSepherosa Ziehau }
6104b3b75d9cSSepherosa Ziehau 
6105b3b75d9cSSepherosa Ziehau static void
6106b3b75d9cSSepherosa Ziehau hn_suspend_data(struct hn_softc *sc)
6107b3b75d9cSSepherosa Ziehau {
610825641fc7SSepherosa Ziehau 	struct hn_tx_ring *txr;
6109b3b75d9cSSepherosa Ziehau 	int i;
611015516c77SSepherosa Ziehau 
611115516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
611215516c77SSepherosa Ziehau 
611315516c77SSepherosa Ziehau 	/*
611415516c77SSepherosa Ziehau 	 * Suspend TX.
611515516c77SSepherosa Ziehau 	 */
611615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
611725641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
611815516c77SSepherosa Ziehau 
611915516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
612015516c77SSepherosa Ziehau 		txr->hn_suspended = 1;
612115516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
612215516c77SSepherosa Ziehau 		/* No one is able send more packets now. */
612315516c77SSepherosa Ziehau 
612425641fc7SSepherosa Ziehau 		/*
612525641fc7SSepherosa Ziehau 		 * Wait for all pending sends to finish.
612625641fc7SSepherosa Ziehau 		 *
612725641fc7SSepherosa Ziehau 		 * NOTE:
612825641fc7SSepherosa Ziehau 		 * We will _not_ receive all pending send-done, if the
612925641fc7SSepherosa Ziehau 		 * primary channel is revoked.
613025641fc7SSepherosa Ziehau 		 */
613125641fc7SSepherosa Ziehau 		while (hn_tx_ring_pending(txr) &&
613225641fc7SSepherosa Ziehau 		    !vmbus_chan_is_revoked(sc->hn_prichan))
613315516c77SSepherosa Ziehau 			pause("hnwtx", 1 /* 1 tick */);
613415516c77SSepherosa Ziehau 	}
613515516c77SSepherosa Ziehau 
613615516c77SSepherosa Ziehau 	/*
6137b3b75d9cSSepherosa Ziehau 	 * Disable RX.
613815516c77SSepherosa Ziehau 	 */
6139b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
614015516c77SSepherosa Ziehau 
614115516c77SSepherosa Ziehau 	/*
6142b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX.
614315516c77SSepherosa Ziehau 	 */
6144b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, sc->hn_rx_ring_inuse);
614525641fc7SSepherosa Ziehau 
614625641fc7SSepherosa Ziehau 	/*
614725641fc7SSepherosa Ziehau 	 * Drain any pending TX tasks.
614825641fc7SSepherosa Ziehau 	 *
614925641fc7SSepherosa Ziehau 	 * NOTE:
6150b3b75d9cSSepherosa Ziehau 	 * The above hn_drain_rxtx() can dispatch TX tasks, so the TX
6151b3b75d9cSSepherosa Ziehau 	 * tasks will have to be drained _after_ the above hn_drain_rxtx().
615225641fc7SSepherosa Ziehau 	 */
615325641fc7SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
615425641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
615525641fc7SSepherosa Ziehau 
615625641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task);
615725641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task);
615825641fc7SSepherosa Ziehau 	}
615915516c77SSepherosa Ziehau }
616015516c77SSepherosa Ziehau 
616115516c77SSepherosa Ziehau static void
616215516c77SSepherosa Ziehau hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused)
616315516c77SSepherosa Ziehau {
616415516c77SSepherosa Ziehau 
616515516c77SSepherosa Ziehau 	((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL;
616615516c77SSepherosa Ziehau }
616715516c77SSepherosa Ziehau 
616815516c77SSepherosa Ziehau static void
616915516c77SSepherosa Ziehau hn_suspend_mgmt(struct hn_softc *sc)
617015516c77SSepherosa Ziehau {
617115516c77SSepherosa Ziehau 	struct task task;
617215516c77SSepherosa Ziehau 
617315516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
617415516c77SSepherosa Ziehau 
617515516c77SSepherosa Ziehau 	/*
617615516c77SSepherosa Ziehau 	 * Make sure that hn_mgmt_taskq0 can nolonger be accessed
617715516c77SSepherosa Ziehau 	 * through hn_mgmt_taskq.
617815516c77SSepherosa Ziehau 	 */
617915516c77SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_suspend_mgmt_taskfunc, sc);
618015516c77SSepherosa Ziehau 	vmbus_chan_run_task(sc->hn_prichan, &task);
618115516c77SSepherosa Ziehau 
618215516c77SSepherosa Ziehau 	/*
618315516c77SSepherosa Ziehau 	 * Make sure that all pending management tasks are completed.
618415516c77SSepherosa Ziehau 	 */
618515516c77SSepherosa Ziehau 	taskqueue_drain(sc->hn_mgmt_taskq0, &sc->hn_netchg_init);
618615516c77SSepherosa Ziehau 	taskqueue_drain_timeout(sc->hn_mgmt_taskq0, &sc->hn_netchg_status);
618715516c77SSepherosa Ziehau 	taskqueue_drain_all(sc->hn_mgmt_taskq0);
618815516c77SSepherosa Ziehau }
618915516c77SSepherosa Ziehau 
619015516c77SSepherosa Ziehau static void
619115516c77SSepherosa Ziehau hn_suspend(struct hn_softc *sc)
619215516c77SSepherosa Ziehau {
619315516c77SSepherosa Ziehau 
619487f8129dSSepherosa Ziehau 	/* Disable polling. */
619587f8129dSSepherosa Ziehau 	hn_polling(sc, 0);
619687f8129dSSepherosa Ziehau 
61979c6cae24SSepherosa Ziehau 	/*
61989c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
61999c6cae24SSepherosa Ziehau 	 * device is receiving packets, so the data path of the
62009c6cae24SSepherosa Ziehau 	 * synthetic device must be suspended.
62019c6cae24SSepherosa Ziehau 	 */
62025bdfd3fdSDexuan Cui 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
6203962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
620415516c77SSepherosa Ziehau 		hn_suspend_data(sc);
620515516c77SSepherosa Ziehau 	hn_suspend_mgmt(sc);
620615516c77SSepherosa Ziehau }
620715516c77SSepherosa Ziehau 
620815516c77SSepherosa Ziehau static void
620915516c77SSepherosa Ziehau hn_resume_tx(struct hn_softc *sc, int tx_ring_cnt)
621015516c77SSepherosa Ziehau {
621115516c77SSepherosa Ziehau 	int i;
621215516c77SSepherosa Ziehau 
621315516c77SSepherosa Ziehau 	KASSERT(tx_ring_cnt <= sc->hn_tx_ring_cnt,
621415516c77SSepherosa Ziehau 	    ("invalid TX ring count %d", tx_ring_cnt));
621515516c77SSepherosa Ziehau 
621615516c77SSepherosa Ziehau 	for (i = 0; i < tx_ring_cnt; ++i) {
621715516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
621815516c77SSepherosa Ziehau 
621915516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
622015516c77SSepherosa Ziehau 		txr->hn_suspended = 0;
622115516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
622215516c77SSepherosa Ziehau 	}
622315516c77SSepherosa Ziehau }
622415516c77SSepherosa Ziehau 
622515516c77SSepherosa Ziehau static void
622615516c77SSepherosa Ziehau hn_resume_data(struct hn_softc *sc)
622715516c77SSepherosa Ziehau {
622815516c77SSepherosa Ziehau 	int i;
622915516c77SSepherosa Ziehau 
623015516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
623115516c77SSepherosa Ziehau 
623215516c77SSepherosa Ziehau 	/*
623315516c77SSepherosa Ziehau 	 * Re-enable RX.
623415516c77SSepherosa Ziehau 	 */
6235c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
623615516c77SSepherosa Ziehau 
623715516c77SSepherosa Ziehau 	/*
623815516c77SSepherosa Ziehau 	 * Make sure to clear suspend status on "all" TX rings,
623915516c77SSepherosa Ziehau 	 * since hn_tx_ring_inuse can be changed after
624015516c77SSepherosa Ziehau 	 * hn_suspend_data().
624115516c77SSepherosa Ziehau 	 */
624215516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_cnt);
624315516c77SSepherosa Ziehau 
624423bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
624523bf9e15SSepherosa Ziehau 	if (!hn_use_if_start)
624623bf9e15SSepherosa Ziehau #endif
624723bf9e15SSepherosa Ziehau 	{
624815516c77SSepherosa Ziehau 		/*
624915516c77SSepherosa Ziehau 		 * Flush unused drbrs, since hn_tx_ring_inuse may be
625015516c77SSepherosa Ziehau 		 * reduced.
625115516c77SSepherosa Ziehau 		 */
625215516c77SSepherosa Ziehau 		for (i = sc->hn_tx_ring_inuse; i < sc->hn_tx_ring_cnt; ++i)
625315516c77SSepherosa Ziehau 			hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
625415516c77SSepherosa Ziehau 	}
625515516c77SSepherosa Ziehau 
625615516c77SSepherosa Ziehau 	/*
625715516c77SSepherosa Ziehau 	 * Kick start TX.
625815516c77SSepherosa Ziehau 	 */
625915516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
626015516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
626115516c77SSepherosa Ziehau 
626215516c77SSepherosa Ziehau 		/*
626315516c77SSepherosa Ziehau 		 * Use txeof task, so that any pending oactive can be
626415516c77SSepherosa Ziehau 		 * cleared properly.
626515516c77SSepherosa Ziehau 		 */
626615516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
626715516c77SSepherosa Ziehau 	}
626815516c77SSepherosa Ziehau }
626915516c77SSepherosa Ziehau 
627015516c77SSepherosa Ziehau static void
627115516c77SSepherosa Ziehau hn_resume_mgmt(struct hn_softc *sc)
627215516c77SSepherosa Ziehau {
627315516c77SSepherosa Ziehau 
627415516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
627515516c77SSepherosa Ziehau 
627615516c77SSepherosa Ziehau 	/*
627715516c77SSepherosa Ziehau 	 * Kick off network change detection, if it was pending.
627815516c77SSepherosa Ziehau 	 * If no network change was pending, start link status
627915516c77SSepherosa Ziehau 	 * checks, which is more lightweight than network change
628015516c77SSepherosa Ziehau 	 * detection.
628115516c77SSepherosa Ziehau 	 */
628215516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
628315516c77SSepherosa Ziehau 		hn_change_network(sc);
628415516c77SSepherosa Ziehau 	else
628515516c77SSepherosa Ziehau 		hn_update_link_status(sc);
628615516c77SSepherosa Ziehau }
628715516c77SSepherosa Ziehau 
628815516c77SSepherosa Ziehau static void
628915516c77SSepherosa Ziehau hn_resume(struct hn_softc *sc)
629015516c77SSepherosa Ziehau {
629115516c77SSepherosa Ziehau 
62929c6cae24SSepherosa Ziehau 	/*
62939c6cae24SSepherosa Ziehau 	 * If the non-transparent mode VF is activated, the synthetic
62949c6cae24SSepherosa Ziehau 	 * device have to receive packets, so the data path of the
62959c6cae24SSepherosa Ziehau 	 * synthetic device must be resumed.
62969c6cae24SSepherosa Ziehau 	 */
62975bdfd3fdSDexuan Cui 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
6298962f0357SSepherosa Ziehau 	    (sc->hn_flags & HN_FLAG_RXVF))
629915516c77SSepherosa Ziehau 		hn_resume_data(sc);
63005bdfd3fdSDexuan Cui 
63015bdfd3fdSDexuan Cui 	/*
63029c6cae24SSepherosa Ziehau 	 * Don't resume link status change if VF is attached/activated.
63039c6cae24SSepherosa Ziehau 	 * - In the non-transparent VF mode, the synthetic device marks
63049c6cae24SSepherosa Ziehau 	 *   link down until the VF is deactivated; i.e. VF is down.
63059c6cae24SSepherosa Ziehau 	 * - In transparent VF mode, VF's media status is used until
63069c6cae24SSepherosa Ziehau 	 *   the VF is detached.
63075bdfd3fdSDexuan Cui 	 */
63089c6cae24SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_RXVF) == 0 &&
63099c6cae24SSepherosa Ziehau 	    !(hn_xpnt_vf && sc->hn_vf_ifp != NULL))
631015516c77SSepherosa Ziehau 		hn_resume_mgmt(sc);
631187f8129dSSepherosa Ziehau 
631287f8129dSSepherosa Ziehau 	/*
631387f8129dSSepherosa Ziehau 	 * Re-enable polling if this interface is running and
631487f8129dSSepherosa Ziehau 	 * the polling is requested.
631587f8129dSSepherosa Ziehau 	 */
631687f8129dSSepherosa Ziehau 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->hn_pollhz > 0)
631787f8129dSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
631815516c77SSepherosa Ziehau }
631915516c77SSepherosa Ziehau 
632015516c77SSepherosa Ziehau static void
632115516c77SSepherosa Ziehau hn_rndis_rx_status(struct hn_softc *sc, const void *data, int dlen)
632215516c77SSepherosa Ziehau {
632315516c77SSepherosa Ziehau 	const struct rndis_status_msg *msg;
632415516c77SSepherosa Ziehau 	int ofs;
632515516c77SSepherosa Ziehau 
632615516c77SSepherosa Ziehau 	if (dlen < sizeof(*msg)) {
632715516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid RNDIS status\n");
632815516c77SSepherosa Ziehau 		return;
632915516c77SSepherosa Ziehau 	}
633015516c77SSepherosa Ziehau 	msg = data;
633115516c77SSepherosa Ziehau 
633215516c77SSepherosa Ziehau 	switch (msg->rm_status) {
633315516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_CONNECT:
633415516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_DISCONNECT:
633515516c77SSepherosa Ziehau 		hn_update_link_status(sc);
633615516c77SSepherosa Ziehau 		break;
633715516c77SSepherosa Ziehau 
633815516c77SSepherosa Ziehau 	case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
633940905afaSSepherosa Ziehau 	case RNDIS_STATUS_LINK_SPEED_CHANGE:
634015516c77SSepherosa Ziehau 		/* Not really useful; ignore. */
634115516c77SSepherosa Ziehau 		break;
634215516c77SSepherosa Ziehau 
634315516c77SSepherosa Ziehau 	case RNDIS_STATUS_NETWORK_CHANGE:
634415516c77SSepherosa Ziehau 		ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
634515516c77SSepherosa Ziehau 		if (dlen < ofs + msg->rm_stbuflen ||
634615516c77SSepherosa Ziehau 		    msg->rm_stbuflen < sizeof(uint32_t)) {
634715516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed\n");
634815516c77SSepherosa Ziehau 		} else {
634915516c77SSepherosa Ziehau 			uint32_t change;
635015516c77SSepherosa Ziehau 
635115516c77SSepherosa Ziehau 			memcpy(&change, ((const uint8_t *)msg) + ofs,
635215516c77SSepherosa Ziehau 			    sizeof(change));
635315516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed, change %u\n",
635415516c77SSepherosa Ziehau 			    change);
635515516c77SSepherosa Ziehau 		}
635615516c77SSepherosa Ziehau 		hn_change_network(sc);
635715516c77SSepherosa Ziehau 		break;
635815516c77SSepherosa Ziehau 
635915516c77SSepherosa Ziehau 	default:
636015516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
636115516c77SSepherosa Ziehau 		    msg->rm_status);
636215516c77SSepherosa Ziehau 		break;
636315516c77SSepherosa Ziehau 	}
636415516c77SSepherosa Ziehau }
636515516c77SSepherosa Ziehau 
636615516c77SSepherosa Ziehau static int
636715516c77SSepherosa Ziehau hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info)
636815516c77SSepherosa Ziehau {
636915516c77SSepherosa Ziehau 	const struct rndis_pktinfo *pi = info_data;
637015516c77SSepherosa Ziehau 	uint32_t mask = 0;
637115516c77SSepherosa Ziehau 
637215516c77SSepherosa Ziehau 	while (info_dlen != 0) {
637315516c77SSepherosa Ziehau 		const void *data;
637415516c77SSepherosa Ziehau 		uint32_t dlen;
637515516c77SSepherosa Ziehau 
637615516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < sizeof(*pi)))
637715516c77SSepherosa Ziehau 			return (EINVAL);
637815516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < pi->rm_size))
637915516c77SSepherosa Ziehau 			return (EINVAL);
638015516c77SSepherosa Ziehau 		info_dlen -= pi->rm_size;
638115516c77SSepherosa Ziehau 
638215516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
638315516c77SSepherosa Ziehau 			return (EINVAL);
638415516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
638515516c77SSepherosa Ziehau 			return (EINVAL);
638615516c77SSepherosa Ziehau 		dlen = pi->rm_size - pi->rm_pktinfooffset;
638715516c77SSepherosa Ziehau 		data = pi->rm_data;
638815516c77SSepherosa Ziehau 
638915516c77SSepherosa Ziehau 		switch (pi->rm_type) {
639015516c77SSepherosa Ziehau 		case NDIS_PKTINFO_TYPE_VLAN:
639115516c77SSepherosa Ziehau 			if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
639215516c77SSepherosa Ziehau 				return (EINVAL);
639315516c77SSepherosa Ziehau 			info->vlan_info = *((const uint32_t *)data);
639415516c77SSepherosa Ziehau 			mask |= HN_RXINFO_VLAN;
639515516c77SSepherosa Ziehau 			break;
639615516c77SSepherosa Ziehau 
639715516c77SSepherosa Ziehau 		case NDIS_PKTINFO_TYPE_CSUM:
639815516c77SSepherosa Ziehau 			if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
639915516c77SSepherosa Ziehau 				return (EINVAL);
640015516c77SSepherosa Ziehau 			info->csum_info = *((const uint32_t *)data);
640115516c77SSepherosa Ziehau 			mask |= HN_RXINFO_CSUM;
640215516c77SSepherosa Ziehau 			break;
640315516c77SSepherosa Ziehau 
640415516c77SSepherosa Ziehau 		case HN_NDIS_PKTINFO_TYPE_HASHVAL:
640515516c77SSepherosa Ziehau 			if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
640615516c77SSepherosa Ziehau 				return (EINVAL);
640715516c77SSepherosa Ziehau 			info->hash_value = *((const uint32_t *)data);
640815516c77SSepherosa Ziehau 			mask |= HN_RXINFO_HASHVAL;
640915516c77SSepherosa Ziehau 			break;
641015516c77SSepherosa Ziehau 
641115516c77SSepherosa Ziehau 		case HN_NDIS_PKTINFO_TYPE_HASHINF:
641215516c77SSepherosa Ziehau 			if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
641315516c77SSepherosa Ziehau 				return (EINVAL);
641415516c77SSepherosa Ziehau 			info->hash_info = *((const uint32_t *)data);
641515516c77SSepherosa Ziehau 			mask |= HN_RXINFO_HASHINF;
641615516c77SSepherosa Ziehau 			break;
641715516c77SSepherosa Ziehau 
641815516c77SSepherosa Ziehau 		default:
641915516c77SSepherosa Ziehau 			goto next;
642015516c77SSepherosa Ziehau 		}
642115516c77SSepherosa Ziehau 
642215516c77SSepherosa Ziehau 		if (mask == HN_RXINFO_ALL) {
642315516c77SSepherosa Ziehau 			/* All found; done */
642415516c77SSepherosa Ziehau 			break;
642515516c77SSepherosa Ziehau 		}
642615516c77SSepherosa Ziehau next:
642715516c77SSepherosa Ziehau 		pi = (const struct rndis_pktinfo *)
642815516c77SSepherosa Ziehau 		    ((const uint8_t *)pi + pi->rm_size);
642915516c77SSepherosa Ziehau 	}
643015516c77SSepherosa Ziehau 
643115516c77SSepherosa Ziehau 	/*
643215516c77SSepherosa Ziehau 	 * Final fixup.
643315516c77SSepherosa Ziehau 	 * - If there is no hash value, invalidate the hash info.
643415516c77SSepherosa Ziehau 	 */
643515516c77SSepherosa Ziehau 	if ((mask & HN_RXINFO_HASHVAL) == 0)
643615516c77SSepherosa Ziehau 		info->hash_info = HN_NDIS_HASH_INFO_INVALID;
643715516c77SSepherosa Ziehau 	return (0);
643815516c77SSepherosa Ziehau }
643915516c77SSepherosa Ziehau 
644015516c77SSepherosa Ziehau static __inline bool
644115516c77SSepherosa Ziehau hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
644215516c77SSepherosa Ziehau {
644315516c77SSepherosa Ziehau 
644415516c77SSepherosa Ziehau 	if (off < check_off) {
644515516c77SSepherosa Ziehau 		if (__predict_true(off + len <= check_off))
644615516c77SSepherosa Ziehau 			return (false);
644715516c77SSepherosa Ziehau 	} else if (off > check_off) {
644815516c77SSepherosa Ziehau 		if (__predict_true(check_off + check_len <= off))
644915516c77SSepherosa Ziehau 			return (false);
645015516c77SSepherosa Ziehau 	}
645115516c77SSepherosa Ziehau 	return (true);
645215516c77SSepherosa Ziehau }
645315516c77SSepherosa Ziehau 
645415516c77SSepherosa Ziehau static void
645515516c77SSepherosa Ziehau hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen)
645615516c77SSepherosa Ziehau {
645715516c77SSepherosa Ziehau 	const struct rndis_packet_msg *pkt;
645815516c77SSepherosa Ziehau 	struct hn_rxinfo info;
645915516c77SSepherosa Ziehau 	int data_off, pktinfo_off, data_len, pktinfo_len;
646015516c77SSepherosa Ziehau 
646115516c77SSepherosa Ziehau 	/*
646215516c77SSepherosa Ziehau 	 * Check length.
646315516c77SSepherosa Ziehau 	 */
646415516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*pkt))) {
646515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
646615516c77SSepherosa Ziehau 		return;
646715516c77SSepherosa Ziehau 	}
646815516c77SSepherosa Ziehau 	pkt = data;
646915516c77SSepherosa Ziehau 
647015516c77SSepherosa Ziehau 	if (__predict_false(dlen < pkt->rm_len)) {
647115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
647215516c77SSepherosa Ziehau 		    "dlen %d, msglen %u\n", dlen, pkt->rm_len);
647315516c77SSepherosa Ziehau 		return;
647415516c77SSepherosa Ziehau 	}
647515516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_len <
647615516c77SSepherosa Ziehau 	    pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
647715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
647815516c77SSepherosa Ziehau 		    "msglen %u, data %u, oob %u, pktinfo %u\n",
647915516c77SSepherosa Ziehau 		    pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
648015516c77SSepherosa Ziehau 		    pkt->rm_pktinfolen);
648115516c77SSepherosa Ziehau 		return;
648215516c77SSepherosa Ziehau 	}
648315516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_datalen == 0)) {
648415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
648515516c77SSepherosa Ziehau 		return;
648615516c77SSepherosa Ziehau 	}
648715516c77SSepherosa Ziehau 
648815516c77SSepherosa Ziehau 	/*
648915516c77SSepherosa Ziehau 	 * Check offests.
649015516c77SSepherosa Ziehau 	 */
649115516c77SSepherosa Ziehau #define IS_OFFSET_INVALID(ofs)			\
649215516c77SSepherosa Ziehau 	((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN ||	\
649315516c77SSepherosa Ziehau 	 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
649415516c77SSepherosa Ziehau 
649515516c77SSepherosa Ziehau 	/* XXX Hyper-V does not meet data offset alignment requirement */
649615516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
649715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
649815516c77SSepherosa Ziehau 		    "data offset %u\n", pkt->rm_dataoffset);
649915516c77SSepherosa Ziehau 		return;
650015516c77SSepherosa Ziehau 	}
650115516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdataoffset > 0 &&
650215516c77SSepherosa Ziehau 	    IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
650315516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
650415516c77SSepherosa Ziehau 		    "oob offset %u\n", pkt->rm_oobdataoffset);
650515516c77SSepherosa Ziehau 		return;
650615516c77SSepherosa Ziehau 	}
650715516c77SSepherosa Ziehau 	if (__predict_true(pkt->rm_pktinfooffset > 0) &&
650815516c77SSepherosa Ziehau 	    __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
650915516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
651015516c77SSepherosa Ziehau 		    "pktinfo offset %u\n", pkt->rm_pktinfooffset);
651115516c77SSepherosa Ziehau 		return;
651215516c77SSepherosa Ziehau 	}
651315516c77SSepherosa Ziehau 
651415516c77SSepherosa Ziehau #undef IS_OFFSET_INVALID
651515516c77SSepherosa Ziehau 
651615516c77SSepherosa Ziehau 	data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
651715516c77SSepherosa Ziehau 	data_len = pkt->rm_datalen;
651815516c77SSepherosa Ziehau 	pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
651915516c77SSepherosa Ziehau 	pktinfo_len = pkt->rm_pktinfolen;
652015516c77SSepherosa Ziehau 
652115516c77SSepherosa Ziehau 	/*
652215516c77SSepherosa Ziehau 	 * Check OOB coverage.
652315516c77SSepherosa Ziehau 	 */
652415516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdatalen != 0)) {
652515516c77SSepherosa Ziehau 		int oob_off, oob_len;
652615516c77SSepherosa Ziehau 
652715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "got oobdata\n");
652815516c77SSepherosa Ziehau 		oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
652915516c77SSepherosa Ziehau 		oob_len = pkt->rm_oobdatalen;
653015516c77SSepherosa Ziehau 
653115516c77SSepherosa Ziehau 		if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
653215516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
653315516c77SSepherosa Ziehau 			    "oob overflow, msglen %u, oob abs %d len %d\n",
653415516c77SSepherosa Ziehau 			    pkt->rm_len, oob_off, oob_len);
653515516c77SSepherosa Ziehau 			return;
653615516c77SSepherosa Ziehau 		}
653715516c77SSepherosa Ziehau 
653815516c77SSepherosa Ziehau 		/*
653915516c77SSepherosa Ziehau 		 * Check against data.
654015516c77SSepherosa Ziehau 		 */
654115516c77SSepherosa Ziehau 		if (hn_rndis_check_overlap(oob_off, oob_len,
654215516c77SSepherosa Ziehau 		    data_off, data_len)) {
654315516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
654415516c77SSepherosa Ziehau 			    "oob overlaps data, oob abs %d len %d, "
654515516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
654615516c77SSepherosa Ziehau 			    oob_off, oob_len, data_off, data_len);
654715516c77SSepherosa Ziehau 			return;
654815516c77SSepherosa Ziehau 		}
654915516c77SSepherosa Ziehau 
655015516c77SSepherosa Ziehau 		/*
655115516c77SSepherosa Ziehau 		 * Check against pktinfo.
655215516c77SSepherosa Ziehau 		 */
655315516c77SSepherosa Ziehau 		if (pktinfo_len != 0 &&
655415516c77SSepherosa Ziehau 		    hn_rndis_check_overlap(oob_off, oob_len,
655515516c77SSepherosa Ziehau 		    pktinfo_off, pktinfo_len)) {
655615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
655715516c77SSepherosa Ziehau 			    "oob overlaps pktinfo, oob abs %d len %d, "
655815516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
655915516c77SSepherosa Ziehau 			    oob_off, oob_len, pktinfo_off, pktinfo_len);
656015516c77SSepherosa Ziehau 			return;
656115516c77SSepherosa Ziehau 		}
656215516c77SSepherosa Ziehau 	}
656315516c77SSepherosa Ziehau 
656415516c77SSepherosa Ziehau 	/*
656515516c77SSepherosa Ziehau 	 * Check per-packet-info coverage and find useful per-packet-info.
656615516c77SSepherosa Ziehau 	 */
656715516c77SSepherosa Ziehau 	info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
656815516c77SSepherosa Ziehau 	info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
656915516c77SSepherosa Ziehau 	info.hash_info = HN_NDIS_HASH_INFO_INVALID;
657015516c77SSepherosa Ziehau 	if (__predict_true(pktinfo_len != 0)) {
657115516c77SSepherosa Ziehau 		bool overlap;
657215516c77SSepherosa Ziehau 		int error;
657315516c77SSepherosa Ziehau 
657415516c77SSepherosa Ziehau 		if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
657515516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
657615516c77SSepherosa Ziehau 			    "pktinfo overflow, msglen %u, "
657715516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
657815516c77SSepherosa Ziehau 			    pkt->rm_len, pktinfo_off, pktinfo_len);
657915516c77SSepherosa Ziehau 			return;
658015516c77SSepherosa Ziehau 		}
658115516c77SSepherosa Ziehau 
658215516c77SSepherosa Ziehau 		/*
658315516c77SSepherosa Ziehau 		 * Check packet info coverage.
658415516c77SSepherosa Ziehau 		 */
658515516c77SSepherosa Ziehau 		overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
658615516c77SSepherosa Ziehau 		    data_off, data_len);
658715516c77SSepherosa Ziehau 		if (__predict_false(overlap)) {
658815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
658915516c77SSepherosa Ziehau 			    "pktinfo overlap data, pktinfo abs %d len %d, "
659015516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
659115516c77SSepherosa Ziehau 			    pktinfo_off, pktinfo_len, data_off, data_len);
659215516c77SSepherosa Ziehau 			return;
659315516c77SSepherosa Ziehau 		}
659415516c77SSepherosa Ziehau 
659515516c77SSepherosa Ziehau 		/*
659615516c77SSepherosa Ziehau 		 * Find useful per-packet-info.
659715516c77SSepherosa Ziehau 		 */
659815516c77SSepherosa Ziehau 		error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
659915516c77SSepherosa Ziehau 		    pktinfo_len, &info);
660015516c77SSepherosa Ziehau 		if (__predict_false(error)) {
660115516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
660215516c77SSepherosa Ziehau 			    "pktinfo\n");
660315516c77SSepherosa Ziehau 			return;
660415516c77SSepherosa Ziehau 		}
660515516c77SSepherosa Ziehau 	}
660615516c77SSepherosa Ziehau 
660715516c77SSepherosa Ziehau 	if (__predict_false(data_off + data_len > pkt->rm_len)) {
660815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
660915516c77SSepherosa Ziehau 		    "data overflow, msglen %u, data abs %d len %d\n",
661015516c77SSepherosa Ziehau 		    pkt->rm_len, data_off, data_len);
661115516c77SSepherosa Ziehau 		return;
661215516c77SSepherosa Ziehau 	}
661315516c77SSepherosa Ziehau 	hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
661415516c77SSepherosa Ziehau }
661515516c77SSepherosa Ziehau 
661615516c77SSepherosa Ziehau static __inline void
661715516c77SSepherosa Ziehau hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen)
661815516c77SSepherosa Ziehau {
661915516c77SSepherosa Ziehau 	const struct rndis_msghdr *hdr;
662015516c77SSepherosa Ziehau 
662115516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*hdr))) {
662215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
662315516c77SSepherosa Ziehau 		return;
662415516c77SSepherosa Ziehau 	}
662515516c77SSepherosa Ziehau 	hdr = data;
662615516c77SSepherosa Ziehau 
662715516c77SSepherosa Ziehau 	if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) {
662815516c77SSepherosa Ziehau 		/* Hot data path. */
662915516c77SSepherosa Ziehau 		hn_rndis_rx_data(rxr, data, dlen);
663015516c77SSepherosa Ziehau 		/* Done! */
663115516c77SSepherosa Ziehau 		return;
663215516c77SSepherosa Ziehau 	}
663315516c77SSepherosa Ziehau 
663415516c77SSepherosa Ziehau 	if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG)
663515516c77SSepherosa Ziehau 		hn_rndis_rx_status(rxr->hn_ifp->if_softc, data, dlen);
663615516c77SSepherosa Ziehau 	else
663715516c77SSepherosa Ziehau 		hn_rndis_rx_ctrl(rxr->hn_ifp->if_softc, data, dlen);
663815516c77SSepherosa Ziehau }
663915516c77SSepherosa Ziehau 
664015516c77SSepherosa Ziehau static void
664115516c77SSepherosa Ziehau hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
664215516c77SSepherosa Ziehau {
664315516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *hdr;
664415516c77SSepherosa Ziehau 
664515516c77SSepherosa Ziehau 	if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) {
664615516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid nvs notify\n");
664715516c77SSepherosa Ziehau 		return;
664815516c77SSepherosa Ziehau 	}
664915516c77SSepherosa Ziehau 	hdr = VMBUS_CHANPKT_CONST_DATA(pkt);
665015516c77SSepherosa Ziehau 
665115516c77SSepherosa Ziehau 	if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) {
665215516c77SSepherosa Ziehau 		/* Useless; ignore */
665315516c77SSepherosa Ziehau 		return;
665415516c77SSepherosa Ziehau 	}
665515516c77SSepherosa Ziehau 	if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type);
665615516c77SSepherosa Ziehau }
665715516c77SSepherosa Ziehau 
665815516c77SSepherosa Ziehau static void
665915516c77SSepherosa Ziehau hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
666015516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkt)
666115516c77SSepherosa Ziehau {
666215516c77SSepherosa Ziehau 	struct hn_nvs_sendctx *sndc;
666315516c77SSepherosa Ziehau 
666415516c77SSepherosa Ziehau 	sndc = (struct hn_nvs_sendctx *)(uintptr_t)pkt->cph_xactid;
666515516c77SSepherosa Ziehau 	sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt),
666615516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_DATALEN(pkt));
666715516c77SSepherosa Ziehau 	/*
666815516c77SSepherosa Ziehau 	 * NOTE:
666915516c77SSepherosa Ziehau 	 * 'sndc' CAN NOT be accessed anymore, since it can be freed by
667015516c77SSepherosa Ziehau 	 * its callback.
667115516c77SSepherosa Ziehau 	 */
667215516c77SSepherosa Ziehau }
667315516c77SSepherosa Ziehau 
667415516c77SSepherosa Ziehau static void
667515516c77SSepherosa Ziehau hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
667615516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkthdr)
667715516c77SSepherosa Ziehau {
667815516c77SSepherosa Ziehau 	const struct vmbus_chanpkt_rxbuf *pkt;
667915516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *nvs_hdr;
668015516c77SSepherosa Ziehau 	int count, i, hlen;
668115516c77SSepherosa Ziehau 
668215516c77SSepherosa Ziehau 	if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) {
668315516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n");
668415516c77SSepherosa Ziehau 		return;
668515516c77SSepherosa Ziehau 	}
668615516c77SSepherosa Ziehau 	nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr);
668715516c77SSepherosa Ziehau 
668815516c77SSepherosa Ziehau 	/* Make sure that this is a RNDIS message. */
668915516c77SSepherosa Ziehau 	if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) {
669015516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n",
669115516c77SSepherosa Ziehau 		    nvs_hdr->nvs_type);
669215516c77SSepherosa Ziehau 		return;
669315516c77SSepherosa Ziehau 	}
669415516c77SSepherosa Ziehau 
669515516c77SSepherosa Ziehau 	hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen);
669615516c77SSepherosa Ziehau 	if (__predict_false(hlen < sizeof(*pkt))) {
669715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n");
669815516c77SSepherosa Ziehau 		return;
669915516c77SSepherosa Ziehau 	}
670015516c77SSepherosa Ziehau 	pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr;
670115516c77SSepherosa Ziehau 
670215516c77SSepherosa Ziehau 	if (__predict_false(pkt->cp_rxbuf_id != HN_NVS_RXBUF_SIG)) {
670315516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n",
670415516c77SSepherosa Ziehau 		    pkt->cp_rxbuf_id);
670515516c77SSepherosa Ziehau 		return;
670615516c77SSepherosa Ziehau 	}
670715516c77SSepherosa Ziehau 
670815516c77SSepherosa Ziehau 	count = pkt->cp_rxbuf_cnt;
670915516c77SSepherosa Ziehau 	if (__predict_false(hlen <
671015516c77SSepherosa Ziehau 	    __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) {
671115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count);
671215516c77SSepherosa Ziehau 		return;
671315516c77SSepherosa Ziehau 	}
671415516c77SSepherosa Ziehau 
671515516c77SSepherosa Ziehau 	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
671615516c77SSepherosa Ziehau 	for (i = 0; i < count; ++i) {
671715516c77SSepherosa Ziehau 		int ofs, len;
671815516c77SSepherosa Ziehau 
671915516c77SSepherosa Ziehau 		ofs = pkt->cp_rxbuf[i].rb_ofs;
672015516c77SSepherosa Ziehau 		len = pkt->cp_rxbuf[i].rb_len;
672115516c77SSepherosa Ziehau 		if (__predict_false(ofs + len > HN_RXBUF_SIZE)) {
672215516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, "
672315516c77SSepherosa Ziehau 			    "ofs %d, len %d\n", i, ofs, len);
672415516c77SSepherosa Ziehau 			continue;
672515516c77SSepherosa Ziehau 		}
672615516c77SSepherosa Ziehau 		hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len);
672715516c77SSepherosa Ziehau 	}
672815516c77SSepherosa Ziehau 
672915516c77SSepherosa Ziehau 	/*
673015516c77SSepherosa Ziehau 	 * Ack the consumed RXBUF associated w/ this channel packet,
673115516c77SSepherosa Ziehau 	 * so that this RXBUF can be recycled by the hypervisor.
673215516c77SSepherosa Ziehau 	 */
673315516c77SSepherosa Ziehau 	hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid);
673415516c77SSepherosa Ziehau }
673515516c77SSepherosa Ziehau 
673615516c77SSepherosa Ziehau static void
673715516c77SSepherosa Ziehau hn_nvs_ack_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
673815516c77SSepherosa Ziehau     uint64_t tid)
673915516c77SSepherosa Ziehau {
674015516c77SSepherosa Ziehau 	struct hn_nvs_rndis_ack ack;
674115516c77SSepherosa Ziehau 	int retries, error;
674215516c77SSepherosa Ziehau 
674315516c77SSepherosa Ziehau 	ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK;
674415516c77SSepherosa Ziehau 	ack.nvs_status = HN_NVS_STATUS_OK;
674515516c77SSepherosa Ziehau 
674615516c77SSepherosa Ziehau 	retries = 0;
674715516c77SSepherosa Ziehau again:
674815516c77SSepherosa Ziehau 	error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
674915516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid);
675015516c77SSepherosa Ziehau 	if (__predict_false(error == EAGAIN)) {
675115516c77SSepherosa Ziehau 		/*
675215516c77SSepherosa Ziehau 		 * NOTE:
675315516c77SSepherosa Ziehau 		 * This should _not_ happen in real world, since the
675415516c77SSepherosa Ziehau 		 * consumption of the TX bufring from the TX path is
675515516c77SSepherosa Ziehau 		 * controlled.
675615516c77SSepherosa Ziehau 		 */
675715516c77SSepherosa Ziehau 		if (rxr->hn_ack_failed == 0)
675815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "RXBUF ack retry\n");
675915516c77SSepherosa Ziehau 		rxr->hn_ack_failed++;
676015516c77SSepherosa Ziehau 		retries++;
676115516c77SSepherosa Ziehau 		if (retries < 10) {
676215516c77SSepherosa Ziehau 			DELAY(100);
676315516c77SSepherosa Ziehau 			goto again;
676415516c77SSepherosa Ziehau 		}
676515516c77SSepherosa Ziehau 		/* RXBUF leaks! */
676615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "RXBUF ack failed\n");
676715516c77SSepherosa Ziehau 	}
676815516c77SSepherosa Ziehau }
676915516c77SSepherosa Ziehau 
677015516c77SSepherosa Ziehau static void
677115516c77SSepherosa Ziehau hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
677215516c77SSepherosa Ziehau {
677315516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr = xrxr;
677415516c77SSepherosa Ziehau 	struct hn_softc *sc = rxr->hn_ifp->if_softc;
677515516c77SSepherosa Ziehau 
677615516c77SSepherosa Ziehau 	for (;;) {
677715516c77SSepherosa Ziehau 		struct vmbus_chanpkt_hdr *pkt = rxr->hn_pktbuf;
677815516c77SSepherosa Ziehau 		int error, pktlen;
677915516c77SSepherosa Ziehau 
678015516c77SSepherosa Ziehau 		pktlen = rxr->hn_pktbuf_len;
678115516c77SSepherosa Ziehau 		error = vmbus_chan_recv_pkt(chan, pkt, &pktlen);
678215516c77SSepherosa Ziehau 		if (__predict_false(error == ENOBUFS)) {
678315516c77SSepherosa Ziehau 			void *nbuf;
678415516c77SSepherosa Ziehau 			int nlen;
678515516c77SSepherosa Ziehau 
678615516c77SSepherosa Ziehau 			/*
678715516c77SSepherosa Ziehau 			 * Expand channel packet buffer.
678815516c77SSepherosa Ziehau 			 *
678915516c77SSepherosa Ziehau 			 * XXX
679015516c77SSepherosa Ziehau 			 * Use M_WAITOK here, since allocation failure
679115516c77SSepherosa Ziehau 			 * is fatal.
679215516c77SSepherosa Ziehau 			 */
679315516c77SSepherosa Ziehau 			nlen = rxr->hn_pktbuf_len * 2;
679415516c77SSepherosa Ziehau 			while (nlen < pktlen)
679515516c77SSepherosa Ziehau 				nlen *= 2;
679615516c77SSepherosa Ziehau 			nbuf = malloc(nlen, M_DEVBUF, M_WAITOK);
679715516c77SSepherosa Ziehau 
679815516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "expand pktbuf %d -> %d\n",
679915516c77SSepherosa Ziehau 			    rxr->hn_pktbuf_len, nlen);
680015516c77SSepherosa Ziehau 
680115516c77SSepherosa Ziehau 			free(rxr->hn_pktbuf, M_DEVBUF);
680215516c77SSepherosa Ziehau 			rxr->hn_pktbuf = nbuf;
680315516c77SSepherosa Ziehau 			rxr->hn_pktbuf_len = nlen;
680415516c77SSepherosa Ziehau 			/* Retry! */
680515516c77SSepherosa Ziehau 			continue;
680615516c77SSepherosa Ziehau 		} else if (__predict_false(error == EAGAIN)) {
680715516c77SSepherosa Ziehau 			/* No more channel packets; done! */
680815516c77SSepherosa Ziehau 			break;
680915516c77SSepherosa Ziehau 		}
681015516c77SSepherosa Ziehau 		KASSERT(!error, ("vmbus_chan_recv_pkt failed: %d", error));
681115516c77SSepherosa Ziehau 
681215516c77SSepherosa Ziehau 		switch (pkt->cph_type) {
681315516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_COMP:
681415516c77SSepherosa Ziehau 			hn_nvs_handle_comp(sc, chan, pkt);
681515516c77SSepherosa Ziehau 			break;
681615516c77SSepherosa Ziehau 
681715516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_RXBUF:
681815516c77SSepherosa Ziehau 			hn_nvs_handle_rxbuf(rxr, chan, pkt);
681915516c77SSepherosa Ziehau 			break;
682015516c77SSepherosa Ziehau 
682115516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_INBAND:
682215516c77SSepherosa Ziehau 			hn_nvs_handle_notify(sc, pkt);
682315516c77SSepherosa Ziehau 			break;
682415516c77SSepherosa Ziehau 
682515516c77SSepherosa Ziehau 		default:
682615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "unknown chan pkt %u\n",
682715516c77SSepherosa Ziehau 			    pkt->cph_type);
682815516c77SSepherosa Ziehau 			break;
682915516c77SSepherosa Ziehau 		}
683015516c77SSepherosa Ziehau 	}
683115516c77SSepherosa Ziehau 	hn_chan_rollup(rxr, rxr->hn_txr);
683215516c77SSepherosa Ziehau }
683315516c77SSepherosa Ziehau 
683415516c77SSepherosa Ziehau static void
6835499c3e17SSepherosa Ziehau hn_sysinit(void *arg __unused)
683615516c77SSepherosa Ziehau {
6837fdd0222aSSepherosa Ziehau 	int i;
6838fdd0222aSSepherosa Ziehau 
68399c6cae24SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
68409c6cae24SSepherosa Ziehau 	/*
68419c6cae24SSepherosa Ziehau 	 * Don't use ifnet.if_start if transparent VF mode is requested;
68429c6cae24SSepherosa Ziehau 	 * mainly due to the IFF_DRV_OACTIVE flag.
68439c6cae24SSepherosa Ziehau 	 */
68449c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf && hn_use_if_start) {
68459c6cae24SSepherosa Ziehau 		hn_use_if_start = 0;
68469c6cae24SSepherosa Ziehau 		printf("hn: tranparent VF mode, if_transmit will be used, "
68479c6cae24SSepherosa Ziehau 		    "instead of if_start\n");
68489c6cae24SSepherosa Ziehau 	}
68499c6cae24SSepherosa Ziehau #endif
68509c6cae24SSepherosa Ziehau 	if (hn_xpnt_vf_attwait < HN_XPNT_VF_ATTWAIT_MIN) {
68519c6cae24SSepherosa Ziehau 		printf("hn: invalid transparent VF attach routing "
68529c6cae24SSepherosa Ziehau 		    "wait timeout %d, reset to %d\n",
68539c6cae24SSepherosa Ziehau 		    hn_xpnt_vf_attwait, HN_XPNT_VF_ATTWAIT_MIN);
68549c6cae24SSepherosa Ziehau 		hn_xpnt_vf_attwait = HN_XPNT_VF_ATTWAIT_MIN;
68559c6cae24SSepherosa Ziehau 	}
68569c6cae24SSepherosa Ziehau 
6857fdd0222aSSepherosa Ziehau 	/*
6858499c3e17SSepherosa Ziehau 	 * Initialize VF map.
6859499c3e17SSepherosa Ziehau 	 */
6860499c3e17SSepherosa Ziehau 	rm_init_flags(&hn_vfmap_lock, "hn_vfmap", RM_SLEEPABLE);
6861499c3e17SSepherosa Ziehau 	hn_vfmap_size = HN_VFMAP_SIZE_DEF;
6862499c3e17SSepherosa Ziehau 	hn_vfmap = malloc(sizeof(struct ifnet *) * hn_vfmap_size, M_DEVBUF,
6863499c3e17SSepherosa Ziehau 	    M_WAITOK | M_ZERO);
6864499c3e17SSepherosa Ziehau 
6865499c3e17SSepherosa Ziehau 	/*
6866fdd0222aSSepherosa Ziehau 	 * Fix the # of TX taskqueues.
6867fdd0222aSSepherosa Ziehau 	 */
6868fdd0222aSSepherosa Ziehau 	if (hn_tx_taskq_cnt <= 0)
6869fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = 1;
6870fdd0222aSSepherosa Ziehau 	else if (hn_tx_taskq_cnt > mp_ncpus)
6871fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = mp_ncpus;
687215516c77SSepherosa Ziehau 
68730e11868dSSepherosa Ziehau 	/*
68740e11868dSSepherosa Ziehau 	 * Fix the TX taskqueue mode.
68750e11868dSSepherosa Ziehau 	 */
68760e11868dSSepherosa Ziehau 	switch (hn_tx_taskq_mode) {
68770e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_INDEP:
68780e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_GLOBAL:
68790e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_EVTTQ:
68800e11868dSSepherosa Ziehau 		break;
68810e11868dSSepherosa Ziehau 	default:
68820e11868dSSepherosa Ziehau 		hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
68830e11868dSSepherosa Ziehau 		break;
68840e11868dSSepherosa Ziehau 	}
68850e11868dSSepherosa Ziehau 
688615516c77SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV)
688715516c77SSepherosa Ziehau 		return;
688815516c77SSepherosa Ziehau 
68890e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode != HN_TX_TASKQ_M_GLOBAL)
689015516c77SSepherosa Ziehau 		return;
689115516c77SSepherosa Ziehau 
6892fdd0222aSSepherosa Ziehau 	hn_tx_taskque = malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
6893fdd0222aSSepherosa Ziehau 	    M_DEVBUF, M_WAITOK);
6894fdd0222aSSepherosa Ziehau 	for (i = 0; i < hn_tx_taskq_cnt; ++i) {
6895fdd0222aSSepherosa Ziehau 		hn_tx_taskque[i] = taskqueue_create("hn_tx", M_WAITOK,
6896fdd0222aSSepherosa Ziehau 		    taskqueue_thread_enqueue, &hn_tx_taskque[i]);
6897fdd0222aSSepherosa Ziehau 		taskqueue_start_threads(&hn_tx_taskque[i], 1, PI_NET,
6898fdd0222aSSepherosa Ziehau 		    "hn tx%d", i);
6899fdd0222aSSepherosa Ziehau 	}
690015516c77SSepherosa Ziehau }
6901499c3e17SSepherosa Ziehau SYSINIT(hn_sysinit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysinit, NULL);
690215516c77SSepherosa Ziehau 
690315516c77SSepherosa Ziehau static void
6904499c3e17SSepherosa Ziehau hn_sysuninit(void *arg __unused)
690515516c77SSepherosa Ziehau {
690615516c77SSepherosa Ziehau 
6907fdd0222aSSepherosa Ziehau 	if (hn_tx_taskque != NULL) {
6908fdd0222aSSepherosa Ziehau 		int i;
6909fdd0222aSSepherosa Ziehau 
6910fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
6911fdd0222aSSepherosa Ziehau 			taskqueue_free(hn_tx_taskque[i]);
6912fdd0222aSSepherosa Ziehau 		free(hn_tx_taskque, M_DEVBUF);
6913fdd0222aSSepherosa Ziehau 	}
6914499c3e17SSepherosa Ziehau 
6915499c3e17SSepherosa Ziehau 	if (hn_vfmap != NULL)
6916499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
6917499c3e17SSepherosa Ziehau 	rm_destroy(&hn_vfmap_lock);
691815516c77SSepherosa Ziehau }
6919499c3e17SSepherosa Ziehau SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL);
6920