xref: /freebsd/sys/dev/hyperv/netvsc/if_hn.c (revision 40905afa0f84819a2d9a54f006a8d9340aa557fb)
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 
12615516c77SSepherosa Ziehau /* YYY should get it from the underlying channel */
12715516c77SSepherosa Ziehau #define HN_TX_DESC_CNT			512
12815516c77SSepherosa Ziehau 
12915516c77SSepherosa Ziehau #define HN_RNDIS_PKT_LEN					\
13015516c77SSepherosa Ziehau 	(sizeof(struct rndis_packet_msg) +			\
13115516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(HN_NDIS_HASH_VALUE_SIZE) +	\
13215516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_VLAN_INFO_SIZE) +		\
13315516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_LSO2_INFO_SIZE) +		\
13415516c77SSepherosa Ziehau 	 HN_RNDIS_PKTINFO_SIZE(NDIS_TXCSUM_INFO_SIZE))
13515516c77SSepherosa Ziehau #define HN_RNDIS_PKT_BOUNDARY		PAGE_SIZE
13615516c77SSepherosa Ziehau #define HN_RNDIS_PKT_ALIGN		CACHE_LINE_SIZE
13715516c77SSepherosa Ziehau 
13815516c77SSepherosa Ziehau #define HN_TX_DATA_BOUNDARY		PAGE_SIZE
13915516c77SSepherosa Ziehau #define HN_TX_DATA_MAXSIZE		IP_MAXPACKET
14015516c77SSepherosa Ziehau #define HN_TX_DATA_SEGSIZE		PAGE_SIZE
14115516c77SSepherosa Ziehau /* -1 for RNDIS packet message */
14215516c77SSepherosa Ziehau #define HN_TX_DATA_SEGCNT_MAX		(HN_GPACNT_MAX - 1)
14315516c77SSepherosa Ziehau 
14415516c77SSepherosa Ziehau #define HN_DIRECT_TX_SIZE_DEF		128
14515516c77SSepherosa Ziehau 
14615516c77SSepherosa Ziehau #define HN_EARLY_TXEOF_THRESH		8
14715516c77SSepherosa Ziehau 
14815516c77SSepherosa Ziehau #define HN_PKTBUF_LEN_DEF		(16 * 1024)
14915516c77SSepherosa Ziehau 
15015516c77SSepherosa Ziehau #define HN_LROENT_CNT_DEF		128
15115516c77SSepherosa Ziehau 
15215516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MULTIRX_DEF	(12 * ETHERMTU)
15315516c77SSepherosa Ziehau #define HN_LRO_LENLIM_DEF		(25 * ETHERMTU)
15415516c77SSepherosa Ziehau /* YYY 2*MTU is a bit rough, but should be good enough. */
15515516c77SSepherosa Ziehau #define HN_LRO_LENLIM_MIN(ifp)		(2 * (ifp)->if_mtu)
15615516c77SSepherosa Ziehau 
15715516c77SSepherosa Ziehau #define HN_LRO_ACKCNT_DEF		1
15815516c77SSepherosa Ziehau 
15915516c77SSepherosa Ziehau #define HN_LOCK_INIT(sc)		\
16015516c77SSepherosa Ziehau 	sx_init(&(sc)->hn_lock, device_get_nameunit((sc)->hn_dev))
16115516c77SSepherosa Ziehau #define HN_LOCK_DESTROY(sc)		sx_destroy(&(sc)->hn_lock)
16215516c77SSepherosa Ziehau #define HN_LOCK_ASSERT(sc)		sx_assert(&(sc)->hn_lock, SA_XLOCKED)
163fdc4f478SSepherosa Ziehau #define HN_LOCK(sc)					\
164fdc4f478SSepherosa Ziehau do {							\
165fdc4f478SSepherosa Ziehau 	while (sx_try_xlock(&(sc)->hn_lock) == 0)	\
166fdc4f478SSepherosa Ziehau 		DELAY(1000);				\
167fdc4f478SSepherosa Ziehau } while (0)
16815516c77SSepherosa Ziehau #define HN_UNLOCK(sc)			sx_xunlock(&(sc)->hn_lock)
16915516c77SSepherosa Ziehau 
17015516c77SSepherosa Ziehau #define HN_CSUM_IP_MASK			(CSUM_IP | CSUM_IP_TCP | CSUM_IP_UDP)
17115516c77SSepherosa Ziehau #define HN_CSUM_IP6_MASK		(CSUM_IP6_TCP | CSUM_IP6_UDP)
17215516c77SSepherosa Ziehau #define HN_CSUM_IP_HWASSIST(sc)		\
17315516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP_MASK)
17415516c77SSepherosa Ziehau #define HN_CSUM_IP6_HWASSIST(sc)	\
17515516c77SSepherosa Ziehau 	((sc)->hn_tx_ring[0].hn_csum_assist & HN_CSUM_IP6_MASK)
17615516c77SSepherosa Ziehau 
177dc13fee6SSepherosa Ziehau #define HN_PKTSIZE_MIN(align)		\
178dc13fee6SSepherosa Ziehau 	roundup2(ETHER_MIN_LEN + ETHER_VLAN_ENCAP_LEN - ETHER_CRC_LEN + \
179dc13fee6SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN, (align))
180dc13fee6SSepherosa Ziehau #define HN_PKTSIZE(m, align)		\
181dc13fee6SSepherosa Ziehau 	roundup2((m)->m_pkthdr.len + HN_RNDIS_PKT_LEN, (align))
182dc13fee6SSepherosa Ziehau 
18334d68912SSepherosa Ziehau #ifdef RSS
18434d68912SSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	rss_getcpu((idx) % rss_getnumbuckets())
18534d68912SSepherosa Ziehau #else
1860e11868dSSepherosa Ziehau #define HN_RING_IDX2CPU(sc, idx)	(((sc)->hn_cpu + (idx)) % mp_ncpus)
18734d68912SSepherosa Ziehau #endif
1880e11868dSSepherosa Ziehau 
18915516c77SSepherosa Ziehau struct hn_txdesc {
19015516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
19115516c77SSepherosa Ziehau 	SLIST_ENTRY(hn_txdesc)		link;
19215516c77SSepherosa Ziehau #endif
193dc13fee6SSepherosa Ziehau 	STAILQ_ENTRY(hn_txdesc)		agg_link;
194dc13fee6SSepherosa Ziehau 
195dc13fee6SSepherosa Ziehau 	/* Aggregated txdescs, in sending order. */
196dc13fee6SSepherosa Ziehau 	STAILQ_HEAD(, hn_txdesc)	agg_list;
197dc13fee6SSepherosa Ziehau 
198dc13fee6SSepherosa Ziehau 	/* The oldest packet, if transmission aggregation happens. */
19915516c77SSepherosa Ziehau 	struct mbuf			*m;
20015516c77SSepherosa Ziehau 	struct hn_tx_ring		*txr;
20115516c77SSepherosa Ziehau 	int				refs;
20215516c77SSepherosa Ziehau 	uint32_t			flags;	/* HN_TXD_FLAG_ */
20315516c77SSepherosa Ziehau 	struct hn_nvs_sendctx		send_ctx;
20415516c77SSepherosa Ziehau 	uint32_t			chim_index;
20515516c77SSepherosa Ziehau 	int				chim_size;
20615516c77SSepherosa Ziehau 
20715516c77SSepherosa Ziehau 	bus_dmamap_t			data_dmap;
20815516c77SSepherosa Ziehau 
20915516c77SSepherosa Ziehau 	bus_addr_t			rndis_pkt_paddr;
21015516c77SSepherosa Ziehau 	struct rndis_packet_msg		*rndis_pkt;
21115516c77SSepherosa Ziehau 	bus_dmamap_t			rndis_pkt_dmap;
21215516c77SSepherosa Ziehau };
21315516c77SSepherosa Ziehau 
21415516c77SSepherosa Ziehau #define HN_TXD_FLAG_ONLIST		0x0001
21515516c77SSepherosa Ziehau #define HN_TXD_FLAG_DMAMAP		0x0002
216dc13fee6SSepherosa Ziehau #define HN_TXD_FLAG_ONAGG		0x0004
21715516c77SSepherosa Ziehau 
21815516c77SSepherosa Ziehau struct hn_rxinfo {
21915516c77SSepherosa Ziehau 	uint32_t			vlan_info;
22015516c77SSepherosa Ziehau 	uint32_t			csum_info;
22115516c77SSepherosa Ziehau 	uint32_t			hash_info;
22215516c77SSepherosa Ziehau 	uint32_t			hash_value;
22315516c77SSepherosa Ziehau };
22415516c77SSepherosa Ziehau 
2255bdfd3fdSDexuan Cui struct hn_update_vf {
2265bdfd3fdSDexuan Cui 	struct hn_rx_ring	*rxr;
2275bdfd3fdSDexuan Cui 	struct ifnet		*vf;
2285bdfd3fdSDexuan Cui };
2295bdfd3fdSDexuan Cui 
23015516c77SSepherosa Ziehau #define HN_RXINFO_VLAN			0x0001
23115516c77SSepherosa Ziehau #define HN_RXINFO_CSUM			0x0002
23215516c77SSepherosa Ziehau #define HN_RXINFO_HASHINF		0x0004
23315516c77SSepherosa Ziehau #define HN_RXINFO_HASHVAL		0x0008
23415516c77SSepherosa Ziehau #define HN_RXINFO_ALL			\
23515516c77SSepherosa Ziehau 	(HN_RXINFO_VLAN |		\
23615516c77SSepherosa Ziehau 	 HN_RXINFO_CSUM |		\
23715516c77SSepherosa Ziehau 	 HN_RXINFO_HASHINF |		\
23815516c77SSepherosa Ziehau 	 HN_RXINFO_HASHVAL)
23915516c77SSepherosa Ziehau 
24015516c77SSepherosa Ziehau #define HN_NDIS_VLAN_INFO_INVALID	0xffffffff
24115516c77SSepherosa Ziehau #define HN_NDIS_RXCSUM_INFO_INVALID	0
24215516c77SSepherosa Ziehau #define HN_NDIS_HASH_INFO_INVALID	0
24315516c77SSepherosa Ziehau 
24415516c77SSepherosa Ziehau static int			hn_probe(device_t);
24515516c77SSepherosa Ziehau static int			hn_attach(device_t);
24615516c77SSepherosa Ziehau static int			hn_detach(device_t);
24715516c77SSepherosa Ziehau static int			hn_shutdown(device_t);
24815516c77SSepherosa Ziehau static void			hn_chan_callback(struct vmbus_channel *,
24915516c77SSepherosa Ziehau 				    void *);
25015516c77SSepherosa Ziehau 
25115516c77SSepherosa Ziehau static void			hn_init(void *);
25215516c77SSepherosa Ziehau static int			hn_ioctl(struct ifnet *, u_long, caddr_t);
25323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
25415516c77SSepherosa Ziehau static void			hn_start(struct ifnet *);
25523bf9e15SSepherosa Ziehau #endif
25615516c77SSepherosa Ziehau static int			hn_transmit(struct ifnet *, struct mbuf *);
25715516c77SSepherosa Ziehau static void			hn_xmit_qflush(struct ifnet *);
25815516c77SSepherosa Ziehau static int			hn_ifmedia_upd(struct ifnet *);
25915516c77SSepherosa Ziehau static void			hn_ifmedia_sts(struct ifnet *,
26015516c77SSepherosa Ziehau 				    struct ifmediareq *);
26115516c77SSepherosa Ziehau 
262499c3e17SSepherosa Ziehau static void			hn_ifnet_event(void *, struct ifnet *, int);
263499c3e17SSepherosa Ziehau static void			hn_ifaddr_event(void *, struct ifnet *);
264499c3e17SSepherosa Ziehau static void			hn_ifnet_attevent(void *, struct ifnet *);
265499c3e17SSepherosa Ziehau static void			hn_ifnet_detevent(void *, struct ifnet *);
266499c3e17SSepherosa Ziehau 
26715516c77SSepherosa Ziehau static int			hn_rndis_rxinfo(const void *, int,
26815516c77SSepherosa Ziehau 				    struct hn_rxinfo *);
26915516c77SSepherosa Ziehau static void			hn_rndis_rx_data(struct hn_rx_ring *,
27015516c77SSepherosa Ziehau 				    const void *, int);
27115516c77SSepherosa Ziehau static void			hn_rndis_rx_status(struct hn_softc *,
27215516c77SSepherosa Ziehau 				    const void *, int);
273b3b75d9cSSepherosa Ziehau static void			hn_rndis_init_fixat(struct hn_softc *, int);
27415516c77SSepherosa Ziehau 
27515516c77SSepherosa Ziehau static void			hn_nvs_handle_notify(struct hn_softc *,
27615516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
27715516c77SSepherosa Ziehau static void			hn_nvs_handle_comp(struct hn_softc *,
27815516c77SSepherosa Ziehau 				    struct vmbus_channel *,
27915516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
28015516c77SSepherosa Ziehau static void			hn_nvs_handle_rxbuf(struct hn_rx_ring *,
28115516c77SSepherosa Ziehau 				    struct vmbus_channel *,
28215516c77SSepherosa Ziehau 				    const struct vmbus_chanpkt_hdr *);
28315516c77SSepherosa Ziehau static void			hn_nvs_ack_rxbuf(struct hn_rx_ring *,
28415516c77SSepherosa Ziehau 				    struct vmbus_channel *, uint64_t);
28515516c77SSepherosa Ziehau 
28615516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
28715516c77SSepherosa Ziehau static int			hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS);
28815516c77SSepherosa Ziehau static int			hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS);
28915516c77SSepherosa Ziehau #endif
29015516c77SSepherosa Ziehau static int			hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS);
29115516c77SSepherosa Ziehau static int			hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS);
29215516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
29315516c77SSepherosa Ziehau static int			hn_rx_stat_int_sysctl(SYSCTL_HANDLER_ARGS);
29415516c77SSepherosa Ziehau #else
29515516c77SSepherosa Ziehau static int			hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS);
29615516c77SSepherosa Ziehau #endif
29715516c77SSepherosa Ziehau static int			hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
29815516c77SSepherosa Ziehau static int			hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS);
29915516c77SSepherosa Ziehau static int			hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS);
30015516c77SSepherosa Ziehau static int			hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS);
30115516c77SSepherosa Ziehau static int			hn_caps_sysctl(SYSCTL_HANDLER_ARGS);
30215516c77SSepherosa Ziehau static int			hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS);
30315516c77SSepherosa Ziehau static int			hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS);
30434d68912SSepherosa Ziehau #ifndef RSS
30515516c77SSepherosa Ziehau static int			hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS);
30615516c77SSepherosa Ziehau static int			hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS);
30734d68912SSepherosa Ziehau #endif
30815516c77SSepherosa Ziehau static int			hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS);
309dc13fee6SSepherosa Ziehau static int			hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS);
310dc13fee6SSepherosa Ziehau static int			hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS);
311dc13fee6SSepherosa Ziehau static int			hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS);
312dc13fee6SSepherosa Ziehau static int			hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
3136c1204dfSSepherosa Ziehau static int			hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
31440d60d6eSDexuan Cui static int			hn_vf_sysctl(SYSCTL_HANDLER_ARGS);
315499c3e17SSepherosa Ziehau static int			hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS);
316499c3e17SSepherosa Ziehau static int			hn_vflist_sysctl(SYSCTL_HANDLER_ARGS);
317499c3e17SSepherosa Ziehau static int			hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS);
31815516c77SSepherosa Ziehau 
3195bdfd3fdSDexuan Cui static void			hn_stop(struct hn_softc *, bool);
32015516c77SSepherosa Ziehau static void			hn_init_locked(struct hn_softc *);
32115516c77SSepherosa Ziehau static int			hn_chan_attach(struct hn_softc *,
32215516c77SSepherosa Ziehau 				    struct vmbus_channel *);
32315516c77SSepherosa Ziehau static void			hn_chan_detach(struct hn_softc *,
32415516c77SSepherosa Ziehau 				    struct vmbus_channel *);
32515516c77SSepherosa Ziehau static int			hn_attach_subchans(struct hn_softc *);
32615516c77SSepherosa Ziehau static void			hn_detach_allchans(struct hn_softc *);
32715516c77SSepherosa Ziehau static void			hn_chan_rollup(struct hn_rx_ring *,
32815516c77SSepherosa Ziehau 				    struct hn_tx_ring *);
32915516c77SSepherosa Ziehau static void			hn_set_ring_inuse(struct hn_softc *, int);
33015516c77SSepherosa Ziehau static int			hn_synth_attach(struct hn_softc *, int);
33115516c77SSepherosa Ziehau static void			hn_synth_detach(struct hn_softc *);
33215516c77SSepherosa Ziehau static int			hn_synth_alloc_subchans(struct hn_softc *,
33315516c77SSepherosa Ziehau 				    int *);
3342494d735SSepherosa Ziehau static bool			hn_synth_attachable(const struct hn_softc *);
33515516c77SSepherosa Ziehau static void			hn_suspend(struct hn_softc *);
33615516c77SSepherosa Ziehau static void			hn_suspend_data(struct hn_softc *);
33715516c77SSepherosa Ziehau static void			hn_suspend_mgmt(struct hn_softc *);
33815516c77SSepherosa Ziehau static void			hn_resume(struct hn_softc *);
33915516c77SSepherosa Ziehau static void			hn_resume_data(struct hn_softc *);
34015516c77SSepherosa Ziehau static void			hn_resume_mgmt(struct hn_softc *);
34115516c77SSepherosa Ziehau static void			hn_suspend_mgmt_taskfunc(void *, int);
34225641fc7SSepherosa Ziehau static void			hn_chan_drain(struct hn_softc *,
34325641fc7SSepherosa Ziehau 				    struct vmbus_channel *);
344b3b75d9cSSepherosa Ziehau static void			hn_disable_rx(struct hn_softc *);
345b3b75d9cSSepherosa Ziehau static void			hn_drain_rxtx(struct hn_softc *, int);
3466c1204dfSSepherosa Ziehau static void			hn_polling(struct hn_softc *, u_int);
3476c1204dfSSepherosa Ziehau static void			hn_chan_polling(struct vmbus_channel *, u_int);
34815516c77SSepherosa Ziehau 
34915516c77SSepherosa Ziehau static void			hn_update_link_status(struct hn_softc *);
35015516c77SSepherosa Ziehau static void			hn_change_network(struct hn_softc *);
35115516c77SSepherosa Ziehau static void			hn_link_taskfunc(void *, int);
35215516c77SSepherosa Ziehau static void			hn_netchg_init_taskfunc(void *, int);
35315516c77SSepherosa Ziehau static void			hn_netchg_status_taskfunc(void *, int);
35415516c77SSepherosa Ziehau static void			hn_link_status(struct hn_softc *);
35515516c77SSepherosa Ziehau 
35615516c77SSepherosa Ziehau static int			hn_create_rx_data(struct hn_softc *, int);
35715516c77SSepherosa Ziehau static void			hn_destroy_rx_data(struct hn_softc *);
35815516c77SSepherosa Ziehau static int			hn_check_iplen(const struct mbuf *, int);
359f1b0a43fSSepherosa Ziehau static int			hn_set_rxfilter(struct hn_softc *, uint32_t);
360c08f7b2cSSepherosa Ziehau static int			hn_rxfilter_config(struct hn_softc *);
36134d68912SSepherosa Ziehau #ifndef RSS
36215516c77SSepherosa Ziehau static int			hn_rss_reconfig(struct hn_softc *);
36334d68912SSepherosa Ziehau #endif
364afd4971bSSepherosa Ziehau static void			hn_rss_ind_fixup(struct hn_softc *);
36515516c77SSepherosa Ziehau static int			hn_rxpkt(struct hn_rx_ring *, const void *,
36615516c77SSepherosa Ziehau 				    int, const struct hn_rxinfo *);
36715516c77SSepherosa Ziehau 
36815516c77SSepherosa Ziehau static int			hn_tx_ring_create(struct hn_softc *, int);
36915516c77SSepherosa Ziehau static void			hn_tx_ring_destroy(struct hn_tx_ring *);
37015516c77SSepherosa Ziehau static int			hn_create_tx_data(struct hn_softc *, int);
37115516c77SSepherosa Ziehau static void			hn_fixup_tx_data(struct hn_softc *);
37215516c77SSepherosa Ziehau static void			hn_destroy_tx_data(struct hn_softc *);
37315516c77SSepherosa Ziehau static void			hn_txdesc_dmamap_destroy(struct hn_txdesc *);
37425641fc7SSepherosa Ziehau static void			hn_txdesc_gc(struct hn_tx_ring *,
37525641fc7SSepherosa Ziehau 				    struct hn_txdesc *);
376dc13fee6SSepherosa Ziehau static int			hn_encap(struct ifnet *, struct hn_tx_ring *,
37715516c77SSepherosa Ziehau 				    struct hn_txdesc *, struct mbuf **);
37815516c77SSepherosa Ziehau static int			hn_txpkt(struct ifnet *, struct hn_tx_ring *,
37915516c77SSepherosa Ziehau 				    struct hn_txdesc *);
38015516c77SSepherosa Ziehau static void			hn_set_chim_size(struct hn_softc *, int);
38115516c77SSepherosa Ziehau static void			hn_set_tso_maxsize(struct hn_softc *, int, int);
38215516c77SSepherosa Ziehau static bool			hn_tx_ring_pending(struct hn_tx_ring *);
38315516c77SSepherosa Ziehau static void			hn_tx_ring_qflush(struct hn_tx_ring *);
38415516c77SSepherosa Ziehau static void			hn_resume_tx(struct hn_softc *, int);
385dc13fee6SSepherosa Ziehau static void			hn_set_txagg(struct hn_softc *);
386dc13fee6SSepherosa Ziehau static void			*hn_try_txagg(struct ifnet *,
387dc13fee6SSepherosa Ziehau 				    struct hn_tx_ring *, struct hn_txdesc *,
388dc13fee6SSepherosa Ziehau 				    int);
38915516c77SSepherosa Ziehau static int			hn_get_txswq_depth(const struct hn_tx_ring *);
39015516c77SSepherosa Ziehau static void			hn_txpkt_done(struct hn_nvs_sendctx *,
39115516c77SSepherosa Ziehau 				    struct hn_softc *, struct vmbus_channel *,
39215516c77SSepherosa Ziehau 				    const void *, int);
39315516c77SSepherosa Ziehau static int			hn_txpkt_sglist(struct hn_tx_ring *,
39415516c77SSepherosa Ziehau 				    struct hn_txdesc *);
39515516c77SSepherosa Ziehau static int			hn_txpkt_chim(struct hn_tx_ring *,
39615516c77SSepherosa Ziehau 				    struct hn_txdesc *);
39715516c77SSepherosa Ziehau static int			hn_xmit(struct hn_tx_ring *, int);
39815516c77SSepherosa Ziehau static void			hn_xmit_taskfunc(void *, int);
39915516c77SSepherosa Ziehau static void			hn_xmit_txeof(struct hn_tx_ring *);
40015516c77SSepherosa Ziehau static void			hn_xmit_txeof_taskfunc(void *, int);
40123bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
40215516c77SSepherosa Ziehau static int			hn_start_locked(struct hn_tx_ring *, int);
40315516c77SSepherosa Ziehau static void			hn_start_taskfunc(void *, int);
40415516c77SSepherosa Ziehau static void			hn_start_txeof(struct hn_tx_ring *);
40515516c77SSepherosa Ziehau static void			hn_start_txeof_taskfunc(void *, int);
40623bf9e15SSepherosa Ziehau #endif
40715516c77SSepherosa Ziehau 
40815516c77SSepherosa Ziehau SYSCTL_NODE(_hw, OID_AUTO, hn, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
40915516c77SSepherosa Ziehau     "Hyper-V network interface");
41015516c77SSepherosa Ziehau 
41115516c77SSepherosa Ziehau /* Trust tcp segements verification on host side. */
41215516c77SSepherosa Ziehau static int			hn_trust_hosttcp = 1;
41315516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hosttcp, CTLFLAG_RDTUN,
41415516c77SSepherosa Ziehau     &hn_trust_hosttcp, 0,
41515516c77SSepherosa Ziehau     "Trust tcp segement verification on host side, "
41615516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
41715516c77SSepherosa Ziehau 
41815516c77SSepherosa Ziehau /* Trust udp datagrams verification on host side. */
41915516c77SSepherosa Ziehau static int			hn_trust_hostudp = 1;
42015516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostudp, CTLFLAG_RDTUN,
42115516c77SSepherosa Ziehau     &hn_trust_hostudp, 0,
42215516c77SSepherosa Ziehau     "Trust udp datagram verification on host side, "
42315516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
42415516c77SSepherosa Ziehau 
42515516c77SSepherosa Ziehau /* Trust ip packets verification on host side. */
42615516c77SSepherosa Ziehau static int			hn_trust_hostip = 1;
42715516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostip, CTLFLAG_RDTUN,
42815516c77SSepherosa Ziehau     &hn_trust_hostip, 0,
42915516c77SSepherosa Ziehau     "Trust ip packet verification on host side, "
43015516c77SSepherosa Ziehau     "when csum info is missing (global setting)");
43115516c77SSepherosa Ziehau 
43215516c77SSepherosa Ziehau /* Limit TSO burst size */
43315516c77SSepherosa Ziehau static int			hn_tso_maxlen = IP_MAXPACKET;
43415516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tso_maxlen, CTLFLAG_RDTUN,
43515516c77SSepherosa Ziehau     &hn_tso_maxlen, 0, "TSO burst limit");
43615516c77SSepherosa Ziehau 
43715516c77SSepherosa Ziehau /* Limit chimney send size */
43815516c77SSepherosa Ziehau static int			hn_tx_chimney_size = 0;
43915516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_chimney_size, CTLFLAG_RDTUN,
44015516c77SSepherosa Ziehau     &hn_tx_chimney_size, 0, "Chimney send packet size limit");
44115516c77SSepherosa Ziehau 
44215516c77SSepherosa Ziehau /* Limit the size of packet for direct transmission */
44315516c77SSepherosa Ziehau static int			hn_direct_tx_size = HN_DIRECT_TX_SIZE_DEF;
44415516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, direct_tx_size, CTLFLAG_RDTUN,
44515516c77SSepherosa Ziehau     &hn_direct_tx_size, 0, "Size of the packet for direct transmission");
44615516c77SSepherosa Ziehau 
44715516c77SSepherosa Ziehau /* # of LRO entries per RX ring */
44815516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
44915516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
45015516c77SSepherosa Ziehau static int			hn_lro_entry_count = HN_LROENT_CNT_DEF;
45115516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, lro_entry_count, CTLFLAG_RDTUN,
45215516c77SSepherosa Ziehau     &hn_lro_entry_count, 0, "LRO entry count");
45315516c77SSepherosa Ziehau #endif
45415516c77SSepherosa Ziehau #endif
45515516c77SSepherosa Ziehau 
456fdd0222aSSepherosa Ziehau static int			hn_tx_taskq_cnt = 1;
457fdd0222aSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_cnt, CTLFLAG_RDTUN,
458fdd0222aSSepherosa Ziehau     &hn_tx_taskq_cnt, 0, "# of TX taskqueues");
459fdd0222aSSepherosa Ziehau 
4600e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_INDEP	0
4610e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_GLOBAL	1
4620e11868dSSepherosa Ziehau #define HN_TX_TASKQ_M_EVTTQ	2
4630e11868dSSepherosa Ziehau 
4640e11868dSSepherosa Ziehau static int			hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
4650e11868dSSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_taskq_mode, CTLFLAG_RDTUN,
4660e11868dSSepherosa Ziehau     &hn_tx_taskq_mode, 0, "TX taskqueue modes: "
4670e11868dSSepherosa Ziehau     "0 - independent, 1 - share global tx taskqs, 2 - share event taskqs");
4680e11868dSSepherosa Ziehau 
46915516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
47015516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 0;
47115516c77SSepherosa Ziehau #else
47215516c77SSepherosa Ziehau static int			hn_use_txdesc_bufring = 1;
47315516c77SSepherosa Ziehau #endif
47415516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_txdesc_bufring, CTLFLAG_RD,
47515516c77SSepherosa Ziehau     &hn_use_txdesc_bufring, 0, "Use buf_ring for TX descriptors");
47615516c77SSepherosa Ziehau 
47723bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
47815516c77SSepherosa Ziehau /* Use ifnet.if_start instead of ifnet.if_transmit */
47915516c77SSepherosa Ziehau static int			hn_use_if_start = 0;
48015516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, use_if_start, CTLFLAG_RDTUN,
48115516c77SSepherosa Ziehau     &hn_use_if_start, 0, "Use if_start TX method");
48223bf9e15SSepherosa Ziehau #endif
48315516c77SSepherosa Ziehau 
48415516c77SSepherosa Ziehau /* # of channels to use */
48515516c77SSepherosa Ziehau static int			hn_chan_cnt = 0;
48615516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, chan_cnt, CTLFLAG_RDTUN,
48715516c77SSepherosa Ziehau     &hn_chan_cnt, 0,
48815516c77SSepherosa Ziehau     "# of channels to use; each channel has one RX ring and one TX ring");
48915516c77SSepherosa Ziehau 
49015516c77SSepherosa Ziehau /* # of transmit rings to use */
49115516c77SSepherosa Ziehau static int			hn_tx_ring_cnt = 0;
49215516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_ring_cnt, CTLFLAG_RDTUN,
49315516c77SSepherosa Ziehau     &hn_tx_ring_cnt, 0, "# of TX rings to use");
49415516c77SSepherosa Ziehau 
49515516c77SSepherosa Ziehau /* Software TX ring deptch */
49615516c77SSepherosa Ziehau static int			hn_tx_swq_depth = 0;
49715516c77SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_swq_depth, CTLFLAG_RDTUN,
49815516c77SSepherosa Ziehau     &hn_tx_swq_depth, 0, "Depth of IFQ or BUFRING");
49915516c77SSepherosa Ziehau 
50015516c77SSepherosa Ziehau /* Enable sorted LRO, and the depth of the per-channel mbuf queue */
50115516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
50215516c77SSepherosa Ziehau static u_int			hn_lro_mbufq_depth = 0;
50315516c77SSepherosa Ziehau SYSCTL_UINT(_hw_hn, OID_AUTO, lro_mbufq_depth, CTLFLAG_RDTUN,
50415516c77SSepherosa Ziehau     &hn_lro_mbufq_depth, 0, "Depth of LRO mbuf queue");
50515516c77SSepherosa Ziehau #endif
50615516c77SSepherosa Ziehau 
507dc13fee6SSepherosa Ziehau /* Packet transmission aggregation size limit */
508dc13fee6SSepherosa Ziehau static int			hn_tx_agg_size = -1;
509dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_size, CTLFLAG_RDTUN,
510dc13fee6SSepherosa Ziehau     &hn_tx_agg_size, 0, "Packet transmission aggregation size limit");
511dc13fee6SSepherosa Ziehau 
512dc13fee6SSepherosa Ziehau /* Packet transmission aggregation count limit */
513fa915c4dSSepherosa Ziehau static int			hn_tx_agg_pkts = -1;
514dc13fee6SSepherosa Ziehau SYSCTL_INT(_hw_hn, OID_AUTO, tx_agg_pkts, CTLFLAG_RDTUN,
515dc13fee6SSepherosa Ziehau     &hn_tx_agg_pkts, 0, "Packet transmission aggregation packet limit");
516dc13fee6SSepherosa Ziehau 
517499c3e17SSepherosa Ziehau /* VF list */
518499c3e17SSepherosa Ziehau SYSCTL_PROC(_hw_hn, OID_AUTO, vflist, CTLFLAG_RD | CTLTYPE_STRING,
519499c3e17SSepherosa Ziehau     0, 0, hn_vflist_sysctl, "A", "VF list");
520499c3e17SSepherosa Ziehau 
521499c3e17SSepherosa Ziehau /* VF mapping */
522499c3e17SSepherosa Ziehau SYSCTL_PROC(_hw_hn, OID_AUTO, vfmap, CTLFLAG_RD | CTLTYPE_STRING,
523499c3e17SSepherosa Ziehau     0, 0, hn_vfmap_sysctl, "A", "VF mapping");
524499c3e17SSepherosa Ziehau 
52515516c77SSepherosa Ziehau static u_int			hn_cpu_index;	/* next CPU for channel */
526fdd0222aSSepherosa Ziehau static struct taskqueue		**hn_tx_taskque;/* shared TX taskqueues */
52715516c77SSepherosa Ziehau 
528499c3e17SSepherosa Ziehau static struct rmlock		hn_vfmap_lock;
529499c3e17SSepherosa Ziehau static int			hn_vfmap_size;
530499c3e17SSepherosa Ziehau static struct ifnet		**hn_vfmap;
531499c3e17SSepherosa Ziehau 
53234d68912SSepherosa Ziehau #ifndef RSS
53315516c77SSepherosa Ziehau static const uint8_t
53415516c77SSepherosa Ziehau hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
53515516c77SSepherosa Ziehau 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
53615516c77SSepherosa Ziehau 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
53715516c77SSepherosa Ziehau 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
53815516c77SSepherosa Ziehau 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
53915516c77SSepherosa Ziehau 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
54015516c77SSepherosa Ziehau };
54134d68912SSepherosa Ziehau #endif	/* !RSS */
54215516c77SSepherosa Ziehau 
54315516c77SSepherosa Ziehau static device_method_t hn_methods[] = {
54415516c77SSepherosa Ziehau 	/* Device interface */
54515516c77SSepherosa Ziehau 	DEVMETHOD(device_probe,		hn_probe),
54615516c77SSepherosa Ziehau 	DEVMETHOD(device_attach,	hn_attach),
54715516c77SSepherosa Ziehau 	DEVMETHOD(device_detach,	hn_detach),
54815516c77SSepherosa Ziehau 	DEVMETHOD(device_shutdown,	hn_shutdown),
54915516c77SSepherosa Ziehau 	DEVMETHOD_END
55015516c77SSepherosa Ziehau };
55115516c77SSepherosa Ziehau 
55215516c77SSepherosa Ziehau static driver_t hn_driver = {
55315516c77SSepherosa Ziehau 	"hn",
55415516c77SSepherosa Ziehau 	hn_methods,
55515516c77SSepherosa Ziehau 	sizeof(struct hn_softc)
55615516c77SSepherosa Ziehau };
55715516c77SSepherosa Ziehau 
55815516c77SSepherosa Ziehau static devclass_t hn_devclass;
55915516c77SSepherosa Ziehau 
56015516c77SSepherosa Ziehau DRIVER_MODULE(hn, vmbus, hn_driver, hn_devclass, 0, 0);
56115516c77SSepherosa Ziehau MODULE_VERSION(hn, 1);
56215516c77SSepherosa Ziehau MODULE_DEPEND(hn, vmbus, 1, 1, 1);
56315516c77SSepherosa Ziehau 
56415516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
56515516c77SSepherosa Ziehau static void
56615516c77SSepherosa Ziehau hn_set_lro_lenlim(struct hn_softc *sc, int lenlim)
56715516c77SSepherosa Ziehau {
56815516c77SSepherosa Ziehau 	int i;
56915516c77SSepherosa Ziehau 
570a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
57115516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_length_lim = lenlim;
57215516c77SSepherosa Ziehau }
57315516c77SSepherosa Ziehau #endif
57415516c77SSepherosa Ziehau 
57515516c77SSepherosa Ziehau static int
57615516c77SSepherosa Ziehau hn_txpkt_sglist(struct hn_tx_ring *txr, struct hn_txdesc *txd)
57715516c77SSepherosa Ziehau {
57815516c77SSepherosa Ziehau 
57915516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
58015516c77SSepherosa Ziehau 	    txd->chim_size == 0, ("invalid rndis sglist txd"));
58115516c77SSepherosa Ziehau 	return (hn_nvs_send_rndis_sglist(txr->hn_chan, HN_NVS_RNDIS_MTYPE_DATA,
58215516c77SSepherosa Ziehau 	    &txd->send_ctx, txr->hn_gpa, txr->hn_gpa_cnt));
58315516c77SSepherosa Ziehau }
58415516c77SSepherosa Ziehau 
58515516c77SSepherosa Ziehau static int
58615516c77SSepherosa Ziehau hn_txpkt_chim(struct hn_tx_ring *txr, struct hn_txdesc *txd)
58715516c77SSepherosa Ziehau {
58815516c77SSepherosa Ziehau 	struct hn_nvs_rndis rndis;
58915516c77SSepherosa Ziehau 
59015516c77SSepherosa Ziehau 	KASSERT(txd->chim_index != HN_NVS_CHIM_IDX_INVALID &&
59115516c77SSepherosa Ziehau 	    txd->chim_size > 0, ("invalid rndis chim txd"));
59215516c77SSepherosa Ziehau 
59315516c77SSepherosa Ziehau 	rndis.nvs_type = HN_NVS_TYPE_RNDIS;
59415516c77SSepherosa Ziehau 	rndis.nvs_rndis_mtype = HN_NVS_RNDIS_MTYPE_DATA;
59515516c77SSepherosa Ziehau 	rndis.nvs_chim_idx = txd->chim_index;
59615516c77SSepherosa Ziehau 	rndis.nvs_chim_sz = txd->chim_size;
59715516c77SSepherosa Ziehau 
59815516c77SSepherosa Ziehau 	return (hn_nvs_send(txr->hn_chan, VMBUS_CHANPKT_FLAG_RC,
59915516c77SSepherosa Ziehau 	    &rndis, sizeof(rndis), &txd->send_ctx));
60015516c77SSepherosa Ziehau }
60115516c77SSepherosa Ziehau 
60215516c77SSepherosa Ziehau static __inline uint32_t
60315516c77SSepherosa Ziehau hn_chim_alloc(struct hn_softc *sc)
60415516c77SSepherosa Ziehau {
60515516c77SSepherosa Ziehau 	int i, bmap_cnt = sc->hn_chim_bmap_cnt;
60615516c77SSepherosa Ziehau 	u_long *bmap = sc->hn_chim_bmap;
60715516c77SSepherosa Ziehau 	uint32_t ret = HN_NVS_CHIM_IDX_INVALID;
60815516c77SSepherosa Ziehau 
60915516c77SSepherosa Ziehau 	for (i = 0; i < bmap_cnt; ++i) {
61015516c77SSepherosa Ziehau 		int idx;
61115516c77SSepherosa Ziehau 
61215516c77SSepherosa Ziehau 		idx = ffsl(~bmap[i]);
61315516c77SSepherosa Ziehau 		if (idx == 0)
61415516c77SSepherosa Ziehau 			continue;
61515516c77SSepherosa Ziehau 
61615516c77SSepherosa Ziehau 		--idx; /* ffsl is 1-based */
61715516c77SSepherosa Ziehau 		KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt,
61815516c77SSepherosa Ziehau 		    ("invalid i %d and idx %d", i, idx));
61915516c77SSepherosa Ziehau 
62015516c77SSepherosa Ziehau 		if (atomic_testandset_long(&bmap[i], idx))
62115516c77SSepherosa Ziehau 			continue;
62215516c77SSepherosa Ziehau 
62315516c77SSepherosa Ziehau 		ret = i * LONG_BIT + idx;
62415516c77SSepherosa Ziehau 		break;
62515516c77SSepherosa Ziehau 	}
62615516c77SSepherosa Ziehau 	return (ret);
62715516c77SSepherosa Ziehau }
62815516c77SSepherosa Ziehau 
62915516c77SSepherosa Ziehau static __inline void
63015516c77SSepherosa Ziehau hn_chim_free(struct hn_softc *sc, uint32_t chim_idx)
63115516c77SSepherosa Ziehau {
63215516c77SSepherosa Ziehau 	u_long mask;
63315516c77SSepherosa Ziehau 	uint32_t idx;
63415516c77SSepherosa Ziehau 
63515516c77SSepherosa Ziehau 	idx = chim_idx / LONG_BIT;
63615516c77SSepherosa Ziehau 	KASSERT(idx < sc->hn_chim_bmap_cnt,
63715516c77SSepherosa Ziehau 	    ("invalid chimney index 0x%x", chim_idx));
63815516c77SSepherosa Ziehau 
63915516c77SSepherosa Ziehau 	mask = 1UL << (chim_idx % LONG_BIT);
64015516c77SSepherosa Ziehau 	KASSERT(sc->hn_chim_bmap[idx] & mask,
64115516c77SSepherosa Ziehau 	    ("index bitmap 0x%lx, chimney index %u, "
64215516c77SSepherosa Ziehau 	     "bitmap idx %d, bitmask 0x%lx",
64315516c77SSepherosa Ziehau 	     sc->hn_chim_bmap[idx], chim_idx, idx, mask));
64415516c77SSepherosa Ziehau 
64515516c77SSepherosa Ziehau 	atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
64615516c77SSepherosa Ziehau }
64715516c77SSepherosa Ziehau 
648edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
649cc0c6ebcSSepherosa Ziehau 
650cc0c6ebcSSepherosa Ziehau #define PULLUP_HDR(m, len)				\
651cc0c6ebcSSepherosa Ziehau do {							\
652cc0c6ebcSSepherosa Ziehau 	if (__predict_false((m)->m_len < (len))) {	\
653cc0c6ebcSSepherosa Ziehau 		(m) = m_pullup((m), (len));		\
654cc0c6ebcSSepherosa Ziehau 		if ((m) == NULL)			\
655cc0c6ebcSSepherosa Ziehau 			return (NULL);			\
656cc0c6ebcSSepherosa Ziehau 	}						\
657cc0c6ebcSSepherosa Ziehau } while (0)
658cc0c6ebcSSepherosa Ziehau 
659edd3f315SSepherosa Ziehau /*
660edd3f315SSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
661edd3f315SSepherosa Ziehau  */
662edd3f315SSepherosa Ziehau static __inline struct mbuf *
663edd3f315SSepherosa Ziehau hn_tso_fixup(struct mbuf *m_head)
664edd3f315SSepherosa Ziehau {
665edd3f315SSepherosa Ziehau 	struct ether_vlan_header *evl;
666edd3f315SSepherosa Ziehau 	struct tcphdr *th;
667edd3f315SSepherosa Ziehau 	int ehlen;
668edd3f315SSepherosa Ziehau 
669edd3f315SSepherosa Ziehau 	KASSERT(M_WRITABLE(m_head), ("TSO mbuf not writable"));
670edd3f315SSepherosa Ziehau 
671edd3f315SSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
672edd3f315SSepherosa Ziehau 	evl = mtod(m_head, struct ether_vlan_header *);
673edd3f315SSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
674edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
675edd3f315SSepherosa Ziehau 	else
676edd3f315SSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
677edd3f315SSepherosa Ziehau 
678edd3f315SSepherosa Ziehau #ifdef INET
679edd3f315SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
680edd3f315SSepherosa Ziehau 		struct ip *ip;
681edd3f315SSepherosa Ziehau 		int iphlen;
682edd3f315SSepherosa Ziehau 
683edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
684edd3f315SSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
685edd3f315SSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
686edd3f315SSepherosa Ziehau 
687edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
688edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + iphlen);
689edd3f315SSepherosa Ziehau 
690edd3f315SSepherosa Ziehau 		ip->ip_len = 0;
691edd3f315SSepherosa Ziehau 		ip->ip_sum = 0;
692edd3f315SSepherosa Ziehau 		th->th_sum = in_pseudo(ip->ip_src.s_addr,
693edd3f315SSepherosa Ziehau 		    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
694edd3f315SSepherosa Ziehau 	}
695edd3f315SSepherosa Ziehau #endif
696edd3f315SSepherosa Ziehau #if defined(INET6) && defined(INET)
697edd3f315SSepherosa Ziehau 	else
698edd3f315SSepherosa Ziehau #endif
699edd3f315SSepherosa Ziehau #ifdef INET6
700edd3f315SSepherosa Ziehau 	{
701edd3f315SSepherosa Ziehau 		struct ip6_hdr *ip6;
702edd3f315SSepherosa Ziehau 
703edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
704edd3f315SSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
705edd3f315SSepherosa Ziehau 		if (ip6->ip6_nxt != IPPROTO_TCP) {
706edd3f315SSepherosa Ziehau 			m_freem(m_head);
707edd3f315SSepherosa Ziehau 			return (NULL);
708edd3f315SSepherosa Ziehau 		}
709edd3f315SSepherosa Ziehau 
710edd3f315SSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
711edd3f315SSepherosa Ziehau 		th = mtodo(m_head, ehlen + sizeof(*ip6));
712edd3f315SSepherosa Ziehau 
713edd3f315SSepherosa Ziehau 		ip6->ip6_plen = 0;
714edd3f315SSepherosa Ziehau 		th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
715edd3f315SSepherosa Ziehau 	}
716edd3f315SSepherosa Ziehau #endif
717edd3f315SSepherosa Ziehau 	return (m_head);
718edd3f315SSepherosa Ziehau 
719edd3f315SSepherosa Ziehau }
720cc0c6ebcSSepherosa Ziehau 
721cc0c6ebcSSepherosa Ziehau /*
722cc0c6ebcSSepherosa Ziehau  * NOTE: If this function failed, the m_head would be freed.
723cc0c6ebcSSepherosa Ziehau  */
724cc0c6ebcSSepherosa Ziehau static __inline struct mbuf *
725cc0c6ebcSSepherosa Ziehau hn_check_tcpsyn(struct mbuf *m_head, int *tcpsyn)
726cc0c6ebcSSepherosa Ziehau {
727cc0c6ebcSSepherosa Ziehau 	const struct ether_vlan_header *evl;
728cc0c6ebcSSepherosa Ziehau 	const struct tcphdr *th;
729cc0c6ebcSSepherosa Ziehau 	int ehlen;
730cc0c6ebcSSepherosa Ziehau 
731cc0c6ebcSSepherosa Ziehau 	*tcpsyn = 0;
732cc0c6ebcSSepherosa Ziehau 
733cc0c6ebcSSepherosa Ziehau 	PULLUP_HDR(m_head, sizeof(*evl));
734cc0c6ebcSSepherosa Ziehau 	evl = mtod(m_head, const struct ether_vlan_header *);
735cc0c6ebcSSepherosa Ziehau 	if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
736cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
737cc0c6ebcSSepherosa Ziehau 	else
738cc0c6ebcSSepherosa Ziehau 		ehlen = ETHER_HDR_LEN;
739cc0c6ebcSSepherosa Ziehau 
740cc0c6ebcSSepherosa Ziehau #ifdef INET
741cc0c6ebcSSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_IP_TCP) {
742cc0c6ebcSSepherosa Ziehau 		const struct ip *ip;
743cc0c6ebcSSepherosa Ziehau 		int iphlen;
744cc0c6ebcSSepherosa Ziehau 
745cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip));
746cc0c6ebcSSepherosa Ziehau 		ip = mtodo(m_head, ehlen);
747cc0c6ebcSSepherosa Ziehau 		iphlen = ip->ip_hl << 2;
748cc0c6ebcSSepherosa Ziehau 
749cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
750cc0c6ebcSSepherosa Ziehau 		th = mtodo(m_head, ehlen + iphlen);
751cc0c6ebcSSepherosa Ziehau 		if (th->th_flags & TH_SYN)
752cc0c6ebcSSepherosa Ziehau 			*tcpsyn = 1;
753cc0c6ebcSSepherosa Ziehau 	}
754cc0c6ebcSSepherosa Ziehau #endif
755cc0c6ebcSSepherosa Ziehau #if defined(INET6) && defined(INET)
756cc0c6ebcSSepherosa Ziehau 	else
757cc0c6ebcSSepherosa Ziehau #endif
758cc0c6ebcSSepherosa Ziehau #ifdef INET6
759cc0c6ebcSSepherosa Ziehau 	{
760cc0c6ebcSSepherosa Ziehau 		const struct ip6_hdr *ip6;
761cc0c6ebcSSepherosa Ziehau 
762cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
763cc0c6ebcSSepherosa Ziehau 		ip6 = mtodo(m_head, ehlen);
764cc0c6ebcSSepherosa Ziehau 		if (ip6->ip6_nxt != IPPROTO_TCP)
765cc0c6ebcSSepherosa Ziehau 			return (m_head);
766cc0c6ebcSSepherosa Ziehau 
767cc0c6ebcSSepherosa Ziehau 		PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
768cc0c6ebcSSepherosa Ziehau 		th = mtodo(m_head, ehlen + sizeof(*ip6));
769cc0c6ebcSSepherosa Ziehau 		if (th->th_flags & TH_SYN)
770cc0c6ebcSSepherosa Ziehau 			*tcpsyn = 1;
771cc0c6ebcSSepherosa Ziehau 	}
772cc0c6ebcSSepherosa Ziehau #endif
773cc0c6ebcSSepherosa Ziehau 	return (m_head);
774cc0c6ebcSSepherosa Ziehau }
775cc0c6ebcSSepherosa Ziehau 
776cc0c6ebcSSepherosa Ziehau #undef PULLUP_HDR
777cc0c6ebcSSepherosa Ziehau 
778edd3f315SSepherosa Ziehau #endif	/* INET6 || INET */
779edd3f315SSepherosa Ziehau 
78015516c77SSepherosa Ziehau static int
781f1b0a43fSSepherosa Ziehau hn_set_rxfilter(struct hn_softc *sc, uint32_t filter)
782f1b0a43fSSepherosa Ziehau {
783f1b0a43fSSepherosa Ziehau 	int error = 0;
784f1b0a43fSSepherosa Ziehau 
785f1b0a43fSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
786f1b0a43fSSepherosa Ziehau 
787f1b0a43fSSepherosa Ziehau 	if (sc->hn_rx_filter != filter) {
788f1b0a43fSSepherosa Ziehau 		error = hn_rndis_set_rxfilter(sc, filter);
789f1b0a43fSSepherosa Ziehau 		if (!error)
790f1b0a43fSSepherosa Ziehau 			sc->hn_rx_filter = filter;
791f1b0a43fSSepherosa Ziehau 	}
792f1b0a43fSSepherosa Ziehau 	return (error);
793f1b0a43fSSepherosa Ziehau }
794f1b0a43fSSepherosa Ziehau 
795f1b0a43fSSepherosa Ziehau static int
796c08f7b2cSSepherosa Ziehau hn_rxfilter_config(struct hn_softc *sc)
79715516c77SSepherosa Ziehau {
79815516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
79915516c77SSepherosa Ziehau 	uint32_t filter;
80015516c77SSepherosa Ziehau 
80115516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
80215516c77SSepherosa Ziehau 
8035bdfd3fdSDexuan Cui 	if ((ifp->if_flags & IFF_PROMISC) ||
8045bdfd3fdSDexuan Cui 	    (sc->hn_flags & HN_FLAG_VF)) {
80515516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
80615516c77SSepherosa Ziehau 	} else {
80715516c77SSepherosa Ziehau 		filter = NDIS_PACKET_TYPE_DIRECTED;
80815516c77SSepherosa Ziehau 		if (ifp->if_flags & IFF_BROADCAST)
80915516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_BROADCAST;
81015516c77SSepherosa Ziehau 		/* TODO: support multicast list */
81115516c77SSepherosa Ziehau 		if ((ifp->if_flags & IFF_ALLMULTI) ||
81215516c77SSepherosa Ziehau 		    !TAILQ_EMPTY(&ifp->if_multiaddrs))
81315516c77SSepherosa Ziehau 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
81415516c77SSepherosa Ziehau 	}
815f1b0a43fSSepherosa Ziehau 	return (hn_set_rxfilter(sc, filter));
81615516c77SSepherosa Ziehau }
81715516c77SSepherosa Ziehau 
818dc13fee6SSepherosa Ziehau static void
819dc13fee6SSepherosa Ziehau hn_set_txagg(struct hn_softc *sc)
820dc13fee6SSepherosa Ziehau {
821dc13fee6SSepherosa Ziehau 	uint32_t size, pkts;
822dc13fee6SSepherosa Ziehau 	int i;
823dc13fee6SSepherosa Ziehau 
824dc13fee6SSepherosa Ziehau 	/*
825dc13fee6SSepherosa Ziehau 	 * Setup aggregation size.
826dc13fee6SSepherosa Ziehau 	 */
827dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_size < 0)
828dc13fee6SSepherosa Ziehau 		size = UINT32_MAX;
829dc13fee6SSepherosa Ziehau 	else
830dc13fee6SSepherosa Ziehau 		size = sc->hn_agg_size;
831dc13fee6SSepherosa Ziehau 
832dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_size < size)
833dc13fee6SSepherosa Ziehau 		size = sc->hn_rndis_agg_size;
834dc13fee6SSepherosa Ziehau 
835a4364cfeSSepherosa Ziehau 	/* NOTE: We only aggregate packets using chimney sending buffers. */
836a4364cfeSSepherosa Ziehau 	if (size > (uint32_t)sc->hn_chim_szmax)
837a4364cfeSSepherosa Ziehau 		size = sc->hn_chim_szmax;
838a4364cfeSSepherosa Ziehau 
839dc13fee6SSepherosa Ziehau 	if (size <= 2 * HN_PKTSIZE_MIN(sc->hn_rndis_agg_align)) {
840dc13fee6SSepherosa Ziehau 		/* Disable */
841dc13fee6SSepherosa Ziehau 		size = 0;
842dc13fee6SSepherosa Ziehau 		pkts = 0;
843dc13fee6SSepherosa Ziehau 		goto done;
844dc13fee6SSepherosa Ziehau 	}
845dc13fee6SSepherosa Ziehau 
846dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'int'. */
847dc13fee6SSepherosa Ziehau 	if (size > INT_MAX)
848dc13fee6SSepherosa Ziehau 		size = INT_MAX;
849dc13fee6SSepherosa Ziehau 
850dc13fee6SSepherosa Ziehau 	/*
851dc13fee6SSepherosa Ziehau 	 * Setup aggregation packet count.
852dc13fee6SSepherosa Ziehau 	 */
853dc13fee6SSepherosa Ziehau 	if (sc->hn_agg_pkts < 0)
854dc13fee6SSepherosa Ziehau 		pkts = UINT32_MAX;
855dc13fee6SSepherosa Ziehau 	else
856dc13fee6SSepherosa Ziehau 		pkts = sc->hn_agg_pkts;
857dc13fee6SSepherosa Ziehau 
858dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_pkts < pkts)
859dc13fee6SSepherosa Ziehau 		pkts = sc->hn_rndis_agg_pkts;
860dc13fee6SSepherosa Ziehau 
861dc13fee6SSepherosa Ziehau 	if (pkts <= 1) {
862dc13fee6SSepherosa Ziehau 		/* Disable */
863dc13fee6SSepherosa Ziehau 		size = 0;
864dc13fee6SSepherosa Ziehau 		pkts = 0;
865dc13fee6SSepherosa Ziehau 		goto done;
866dc13fee6SSepherosa Ziehau 	}
867dc13fee6SSepherosa Ziehau 
868dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
869dc13fee6SSepherosa Ziehau 	if (pkts > SHRT_MAX)
870dc13fee6SSepherosa Ziehau 		pkts = SHRT_MAX;
871dc13fee6SSepherosa Ziehau 
872dc13fee6SSepherosa Ziehau done:
873dc13fee6SSepherosa Ziehau 	/* NOTE: Type of the per TX ring setting is 'short'. */
874dc13fee6SSepherosa Ziehau 	if (sc->hn_rndis_agg_align > SHRT_MAX) {
875dc13fee6SSepherosa Ziehau 		/* Disable */
876dc13fee6SSepherosa Ziehau 		size = 0;
877dc13fee6SSepherosa Ziehau 		pkts = 0;
878dc13fee6SSepherosa Ziehau 	}
879dc13fee6SSepherosa Ziehau 
880dc13fee6SSepherosa Ziehau 	if (bootverbose) {
881dc13fee6SSepherosa Ziehau 		if_printf(sc->hn_ifp, "TX agg size %u, pkts %u, align %u\n",
882dc13fee6SSepherosa Ziehau 		    size, pkts, sc->hn_rndis_agg_align);
883dc13fee6SSepherosa Ziehau 	}
884dc13fee6SSepherosa Ziehau 
885dc13fee6SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
886dc13fee6SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
887dc13fee6SSepherosa Ziehau 
888dc13fee6SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
889dc13fee6SSepherosa Ziehau 		txr->hn_agg_szmax = size;
890dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktmax = pkts;
891dc13fee6SSepherosa Ziehau 		txr->hn_agg_align = sc->hn_rndis_agg_align;
892dc13fee6SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
893dc13fee6SSepherosa Ziehau 	}
894dc13fee6SSepherosa Ziehau }
895dc13fee6SSepherosa Ziehau 
89615516c77SSepherosa Ziehau static int
89715516c77SSepherosa Ziehau hn_get_txswq_depth(const struct hn_tx_ring *txr)
89815516c77SSepherosa Ziehau {
89915516c77SSepherosa Ziehau 
90015516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_cnt > 0, ("tx ring is not setup yet"));
90115516c77SSepherosa Ziehau 	if (hn_tx_swq_depth < txr->hn_txdesc_cnt)
90215516c77SSepherosa Ziehau 		return txr->hn_txdesc_cnt;
90315516c77SSepherosa Ziehau 	return hn_tx_swq_depth;
90415516c77SSepherosa Ziehau }
90515516c77SSepherosa Ziehau 
90634d68912SSepherosa Ziehau #ifndef RSS
90715516c77SSepherosa Ziehau static int
90815516c77SSepherosa Ziehau hn_rss_reconfig(struct hn_softc *sc)
90915516c77SSepherosa Ziehau {
91015516c77SSepherosa Ziehau 	int error;
91115516c77SSepherosa Ziehau 
91215516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
91315516c77SSepherosa Ziehau 
91415516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
91515516c77SSepherosa Ziehau 		return (ENXIO);
91615516c77SSepherosa Ziehau 
91715516c77SSepherosa Ziehau 	/*
91815516c77SSepherosa Ziehau 	 * Disable RSS first.
91915516c77SSepherosa Ziehau 	 *
92015516c77SSepherosa Ziehau 	 * NOTE:
92115516c77SSepherosa Ziehau 	 * Direct reconfiguration by setting the UNCHG flags does
92215516c77SSepherosa Ziehau 	 * _not_ work properly.
92315516c77SSepherosa Ziehau 	 */
92415516c77SSepherosa Ziehau 	if (bootverbose)
92515516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "disable RSS\n");
92615516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_DISABLE);
92715516c77SSepherosa Ziehau 	if (error) {
92815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS disable failed\n");
92915516c77SSepherosa Ziehau 		return (error);
93015516c77SSepherosa Ziehau 	}
93115516c77SSepherosa Ziehau 
93215516c77SSepherosa Ziehau 	/*
93315516c77SSepherosa Ziehau 	 * Reenable the RSS w/ the updated RSS key or indirect
93415516c77SSepherosa Ziehau 	 * table.
93515516c77SSepherosa Ziehau 	 */
93615516c77SSepherosa Ziehau 	if (bootverbose)
93715516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "reconfig RSS\n");
93815516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
93915516c77SSepherosa Ziehau 	if (error) {
94015516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RSS reconfig failed\n");
94115516c77SSepherosa Ziehau 		return (error);
94215516c77SSepherosa Ziehau 	}
94315516c77SSepherosa Ziehau 	return (0);
94415516c77SSepherosa Ziehau }
94534d68912SSepherosa Ziehau #endif	/* !RSS */
94615516c77SSepherosa Ziehau 
94715516c77SSepherosa Ziehau static void
948afd4971bSSepherosa Ziehau hn_rss_ind_fixup(struct hn_softc *sc)
94915516c77SSepherosa Ziehau {
95015516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
951afd4971bSSepherosa Ziehau 	int i, nchan;
95215516c77SSepherosa Ziehau 
953afd4971bSSepherosa Ziehau 	nchan = sc->hn_rx_ring_inuse;
95415516c77SSepherosa Ziehau 	KASSERT(nchan > 1, ("invalid # of channels %d", nchan));
95515516c77SSepherosa Ziehau 
95615516c77SSepherosa Ziehau 	/*
95715516c77SSepherosa Ziehau 	 * Check indirect table to make sure that all channels in it
95815516c77SSepherosa Ziehau 	 * can be used.
95915516c77SSepherosa Ziehau 	 */
96015516c77SSepherosa Ziehau 	for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
96115516c77SSepherosa Ziehau 		if (rss->rss_ind[i] >= nchan) {
96215516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp,
96315516c77SSepherosa Ziehau 			    "RSS indirect table %d fixup: %u -> %d\n",
96415516c77SSepherosa Ziehau 			    i, rss->rss_ind[i], nchan - 1);
96515516c77SSepherosa Ziehau 			rss->rss_ind[i] = nchan - 1;
96615516c77SSepherosa Ziehau 		}
96715516c77SSepherosa Ziehau 	}
96815516c77SSepherosa Ziehau }
96915516c77SSepherosa Ziehau 
97015516c77SSepherosa Ziehau static int
97115516c77SSepherosa Ziehau hn_ifmedia_upd(struct ifnet *ifp __unused)
97215516c77SSepherosa Ziehau {
97315516c77SSepherosa Ziehau 
97415516c77SSepherosa Ziehau 	return EOPNOTSUPP;
97515516c77SSepherosa Ziehau }
97615516c77SSepherosa Ziehau 
97715516c77SSepherosa Ziehau static void
97815516c77SSepherosa Ziehau hn_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
97915516c77SSepherosa Ziehau {
98015516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
98115516c77SSepherosa Ziehau 
98215516c77SSepherosa Ziehau 	ifmr->ifm_status = IFM_AVALID;
98315516c77SSepherosa Ziehau 	ifmr->ifm_active = IFM_ETHER;
98415516c77SSepherosa Ziehau 
98515516c77SSepherosa Ziehau 	if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) {
98615516c77SSepherosa Ziehau 		ifmr->ifm_active |= IFM_NONE;
98715516c77SSepherosa Ziehau 		return;
98815516c77SSepherosa Ziehau 	}
98915516c77SSepherosa Ziehau 	ifmr->ifm_status |= IFM_ACTIVE;
99015516c77SSepherosa Ziehau 	ifmr->ifm_active |= IFM_10G_T | IFM_FDX;
99115516c77SSepherosa Ziehau }
99215516c77SSepherosa Ziehau 
9935bdfd3fdSDexuan Cui static void
9945bdfd3fdSDexuan Cui hn_update_vf_task(void *arg, int pending __unused)
9955bdfd3fdSDexuan Cui {
9965bdfd3fdSDexuan Cui 	struct hn_update_vf *uv = arg;
9975bdfd3fdSDexuan Cui 
998499c3e17SSepherosa Ziehau 	uv->rxr->hn_rxvf_ifp = uv->vf;
9995bdfd3fdSDexuan Cui }
10005bdfd3fdSDexuan Cui 
10015bdfd3fdSDexuan Cui static void
10025bdfd3fdSDexuan Cui hn_update_vf(struct hn_softc *sc, struct ifnet *vf)
10035bdfd3fdSDexuan Cui {
10045bdfd3fdSDexuan Cui 	struct hn_rx_ring *rxr;
10055bdfd3fdSDexuan Cui 	struct hn_update_vf uv;
10065bdfd3fdSDexuan Cui 	struct task task;
10075bdfd3fdSDexuan Cui 	int i;
10085bdfd3fdSDexuan Cui 
10095bdfd3fdSDexuan Cui 	HN_LOCK_ASSERT(sc);
10105bdfd3fdSDexuan Cui 
10115bdfd3fdSDexuan Cui 	TASK_INIT(&task, 0, hn_update_vf_task, &uv);
10125bdfd3fdSDexuan Cui 
10135bdfd3fdSDexuan Cui 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
10145bdfd3fdSDexuan Cui 		rxr = &sc->hn_rx_ring[i];
10155bdfd3fdSDexuan Cui 
10165bdfd3fdSDexuan Cui 		if (i < sc->hn_rx_ring_inuse) {
10175bdfd3fdSDexuan Cui 			uv.rxr = rxr;
10185bdfd3fdSDexuan Cui 			uv.vf = vf;
10195bdfd3fdSDexuan Cui 			vmbus_chan_run_task(rxr->hn_chan, &task);
10205bdfd3fdSDexuan Cui 		} else {
1021499c3e17SSepherosa Ziehau 			rxr->hn_rxvf_ifp = vf;
10225bdfd3fdSDexuan Cui 		}
10235bdfd3fdSDexuan Cui 	}
10245bdfd3fdSDexuan Cui }
10255bdfd3fdSDexuan Cui 
1026499c3e17SSepherosa Ziehau static __inline bool
1027499c3e17SSepherosa Ziehau hn_ismyvf(const struct hn_softc *sc, const struct ifnet *ifp)
1028499c3e17SSepherosa Ziehau {
1029499c3e17SSepherosa Ziehau 	const struct ifnet *hn_ifp;
1030499c3e17SSepherosa Ziehau 
1031499c3e17SSepherosa Ziehau 	hn_ifp = sc->hn_ifp;
1032499c3e17SSepherosa Ziehau 
1033499c3e17SSepherosa Ziehau 	if (ifp == hn_ifp)
1034499c3e17SSepherosa Ziehau 		return (false);
1035499c3e17SSepherosa Ziehau 
1036499c3e17SSepherosa Ziehau 	if (ifp->if_alloctype != IFT_ETHER)
1037499c3e17SSepherosa Ziehau 		return (false);
1038499c3e17SSepherosa Ziehau 
1039499c3e17SSepherosa Ziehau 	/* Ignore lagg/vlan interfaces */
1040499c3e17SSepherosa Ziehau 	if (strcmp(ifp->if_dname, "lagg") == 0 ||
1041499c3e17SSepherosa Ziehau 	    strcmp(ifp->if_dname, "vlan") == 0)
1042499c3e17SSepherosa Ziehau 		return (false);
1043499c3e17SSepherosa Ziehau 
1044499c3e17SSepherosa Ziehau 	if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0)
1045499c3e17SSepherosa Ziehau 		return (false);
1046499c3e17SSepherosa Ziehau 
1047499c3e17SSepherosa Ziehau 	return (true);
1048499c3e17SSepherosa Ziehau }
1049499c3e17SSepherosa Ziehau 
10505bdfd3fdSDexuan Cui static void
10515bdfd3fdSDexuan Cui hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf)
10525bdfd3fdSDexuan Cui {
10535bdfd3fdSDexuan Cui 	struct ifnet *hn_ifp;
10545bdfd3fdSDexuan Cui 
10555bdfd3fdSDexuan Cui 	HN_LOCK(sc);
10565bdfd3fdSDexuan Cui 
10575bdfd3fdSDexuan Cui 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
10585bdfd3fdSDexuan Cui 		goto out;
10595bdfd3fdSDexuan Cui 
1060499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1061499c3e17SSepherosa Ziehau 		goto out;
1062499c3e17SSepherosa Ziehau 
10635bdfd3fdSDexuan Cui 	hn_ifp = sc->hn_ifp;
10645bdfd3fdSDexuan Cui 
10655bdfd3fdSDexuan Cui 	/* Now we're sure 'ifp' is a real VF device. */
10665bdfd3fdSDexuan Cui 	if (vf) {
10675bdfd3fdSDexuan Cui 		if (sc->hn_flags & HN_FLAG_VF)
10685bdfd3fdSDexuan Cui 			goto out;
10695bdfd3fdSDexuan Cui 
10705bdfd3fdSDexuan Cui 		sc->hn_flags |= HN_FLAG_VF;
10715bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
10725bdfd3fdSDexuan Cui 	} else {
10735bdfd3fdSDexuan Cui 		if (!(sc->hn_flags & HN_FLAG_VF))
10745bdfd3fdSDexuan Cui 			goto out;
10755bdfd3fdSDexuan Cui 
10765bdfd3fdSDexuan Cui 		sc->hn_flags &= ~HN_FLAG_VF;
1077499c3e17SSepherosa Ziehau 		if (hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
10785bdfd3fdSDexuan Cui 			hn_rxfilter_config(sc);
10795bdfd3fdSDexuan Cui 		else
10805bdfd3fdSDexuan Cui 			hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE);
10815bdfd3fdSDexuan Cui 	}
10825bdfd3fdSDexuan Cui 
10835bdfd3fdSDexuan Cui 	hn_nvs_set_datapath(sc,
10845bdfd3fdSDexuan Cui 	    vf ? HN_NVS_DATAPATH_VF : HN_NVS_DATAPATH_SYNTHETIC);
10855bdfd3fdSDexuan Cui 
10865bdfd3fdSDexuan Cui 	hn_update_vf(sc, vf ? ifp : NULL);
10875bdfd3fdSDexuan Cui 
10885bdfd3fdSDexuan Cui 	if (vf) {
10895bdfd3fdSDexuan Cui 		hn_suspend_mgmt(sc);
10905bdfd3fdSDexuan Cui 		sc->hn_link_flags &=
10915bdfd3fdSDexuan Cui 		    ~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG);
1092499c3e17SSepherosa Ziehau 		if_link_state_change(hn_ifp, LINK_STATE_DOWN);
10935bdfd3fdSDexuan Cui 	} else {
10945bdfd3fdSDexuan Cui 		hn_resume_mgmt(sc);
10955bdfd3fdSDexuan Cui 	}
10965bdfd3fdSDexuan Cui 
109733408a34SDexuan Cui 	devctl_notify("HYPERV_NIC_VF", if_name(hn_ifp),
109833408a34SDexuan Cui 	    vf ? "VF_UP" : "VF_DOWN", NULL);
109933408a34SDexuan Cui 
11005bdfd3fdSDexuan Cui 	if (bootverbose)
11015bdfd3fdSDexuan Cui 		if_printf(hn_ifp, "Data path is switched %s %s\n",
11025bdfd3fdSDexuan Cui 		    vf ? "to" : "from", if_name(ifp));
11035bdfd3fdSDexuan Cui out:
11045bdfd3fdSDexuan Cui 	HN_UNLOCK(sc);
11055bdfd3fdSDexuan Cui }
11065bdfd3fdSDexuan Cui 
11075bdfd3fdSDexuan Cui static void
11085bdfd3fdSDexuan Cui hn_ifnet_event(void *arg, struct ifnet *ifp, int event)
11095bdfd3fdSDexuan Cui {
11105bdfd3fdSDexuan Cui 	if (event != IFNET_EVENT_UP && event != IFNET_EVENT_DOWN)
11115bdfd3fdSDexuan Cui 		return;
11125bdfd3fdSDexuan Cui 
11135bdfd3fdSDexuan Cui 	hn_set_vf(arg, ifp, event == IFNET_EVENT_UP);
11145bdfd3fdSDexuan Cui }
11155bdfd3fdSDexuan Cui 
11165bdfd3fdSDexuan Cui static void
11175bdfd3fdSDexuan Cui hn_ifaddr_event(void *arg, struct ifnet *ifp)
11185bdfd3fdSDexuan Cui {
11195bdfd3fdSDexuan Cui 	hn_set_vf(arg, ifp, ifp->if_flags & IFF_UP);
11205bdfd3fdSDexuan Cui }
11215bdfd3fdSDexuan Cui 
1122499c3e17SSepherosa Ziehau static void
1123499c3e17SSepherosa Ziehau hn_ifnet_attevent(void *xsc, struct ifnet *ifp)
1124499c3e17SSepherosa Ziehau {
1125499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1126499c3e17SSepherosa Ziehau 
1127499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1128499c3e17SSepherosa Ziehau 
1129499c3e17SSepherosa Ziehau 	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
1130499c3e17SSepherosa Ziehau 		goto done;
1131499c3e17SSepherosa Ziehau 
1132499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1133499c3e17SSepherosa Ziehau 		goto done;
1134499c3e17SSepherosa Ziehau 
1135499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp != NULL) {
1136499c3e17SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%s was attached as VF\n",
1137499c3e17SSepherosa Ziehau 		    sc->hn_vf_ifp->if_xname);
1138499c3e17SSepherosa Ziehau 		goto done;
1139499c3e17SSepherosa Ziehau 	}
1140499c3e17SSepherosa Ziehau 
1141499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
1142499c3e17SSepherosa Ziehau 
1143499c3e17SSepherosa Ziehau 	if (ifp->if_index >= hn_vfmap_size) {
1144499c3e17SSepherosa Ziehau 		struct ifnet **newmap;
1145499c3e17SSepherosa Ziehau 		int newsize;
1146499c3e17SSepherosa Ziehau 
1147499c3e17SSepherosa Ziehau 		newsize = ifp->if_index + HN_VFMAP_SIZE_DEF;
1148499c3e17SSepherosa Ziehau 		newmap = malloc(sizeof(struct ifnet *) * newsize, M_DEVBUF,
1149499c3e17SSepherosa Ziehau 		    M_WAITOK | M_ZERO);
1150499c3e17SSepherosa Ziehau 
1151499c3e17SSepherosa Ziehau 		memcpy(newmap, hn_vfmap,
1152499c3e17SSepherosa Ziehau 		    sizeof(struct ifnet *) * hn_vfmap_size);
1153499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
1154499c3e17SSepherosa Ziehau 		hn_vfmap = newmap;
1155499c3e17SSepherosa Ziehau 		hn_vfmap_size = newsize;
1156499c3e17SSepherosa Ziehau 	}
1157499c3e17SSepherosa Ziehau 	KASSERT(hn_vfmap[ifp->if_index] == NULL,
1158499c3e17SSepherosa Ziehau 	    ("%s: ifindex %d was mapped to %s",
1159499c3e17SSepherosa Ziehau 	     ifp->if_xname, ifp->if_index, hn_vfmap[ifp->if_index]->if_xname));
1160499c3e17SSepherosa Ziehau 	hn_vfmap[ifp->if_index] = sc->hn_ifp;
1161499c3e17SSepherosa Ziehau 
1162499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
1163499c3e17SSepherosa Ziehau 
1164499c3e17SSepherosa Ziehau 	sc->hn_vf_ifp = ifp;
1165499c3e17SSepherosa Ziehau done:
1166499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
1167499c3e17SSepherosa Ziehau }
1168499c3e17SSepherosa Ziehau 
1169499c3e17SSepherosa Ziehau static void
1170499c3e17SSepherosa Ziehau hn_ifnet_detevent(void *xsc, struct ifnet *ifp)
1171499c3e17SSepherosa Ziehau {
1172499c3e17SSepherosa Ziehau 	struct hn_softc *sc = xsc;
1173499c3e17SSepherosa Ziehau 
1174499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
1175499c3e17SSepherosa Ziehau 
1176499c3e17SSepherosa Ziehau 	if (sc->hn_vf_ifp == NULL)
1177499c3e17SSepherosa Ziehau 		goto done;
1178499c3e17SSepherosa Ziehau 
1179499c3e17SSepherosa Ziehau 	if (!hn_ismyvf(sc, ifp))
1180499c3e17SSepherosa Ziehau 		goto done;
1181499c3e17SSepherosa Ziehau 
1182499c3e17SSepherosa Ziehau 	sc->hn_vf_ifp = NULL;
1183499c3e17SSepherosa Ziehau 
1184499c3e17SSepherosa Ziehau 	rm_wlock(&hn_vfmap_lock);
1185499c3e17SSepherosa Ziehau 
1186499c3e17SSepherosa Ziehau 	KASSERT(ifp->if_index < hn_vfmap_size,
1187499c3e17SSepherosa Ziehau 	    ("ifindex %d, vfmapsize %d", ifp->if_index, hn_vfmap_size));
1188499c3e17SSepherosa Ziehau 	if (hn_vfmap[ifp->if_index] != NULL) {
1189499c3e17SSepherosa Ziehau 		KASSERT(hn_vfmap[ifp->if_index] == sc->hn_ifp,
1190499c3e17SSepherosa Ziehau 		    ("%s: ifindex %d was mapped to %s",
1191499c3e17SSepherosa Ziehau 		     ifp->if_xname, ifp->if_index,
1192499c3e17SSepherosa Ziehau 		     hn_vfmap[ifp->if_index]->if_xname));
1193499c3e17SSepherosa Ziehau 		hn_vfmap[ifp->if_index] = NULL;
1194499c3e17SSepherosa Ziehau 	}
1195499c3e17SSepherosa Ziehau 
1196499c3e17SSepherosa Ziehau 	rm_wunlock(&hn_vfmap_lock);
1197499c3e17SSepherosa Ziehau done:
1198499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
1199499c3e17SSepherosa Ziehau }
1200499c3e17SSepherosa Ziehau 
120115516c77SSepherosa Ziehau /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
120215516c77SSepherosa Ziehau static const struct hyperv_guid g_net_vsc_device_type = {
120315516c77SSepherosa Ziehau 	.hv_guid = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
120415516c77SSepherosa Ziehau 		0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}
120515516c77SSepherosa Ziehau };
120615516c77SSepherosa Ziehau 
120715516c77SSepherosa Ziehau static int
120815516c77SSepherosa Ziehau hn_probe(device_t dev)
120915516c77SSepherosa Ziehau {
121015516c77SSepherosa Ziehau 
121115516c77SSepherosa Ziehau 	if (VMBUS_PROBE_GUID(device_get_parent(dev), dev,
121215516c77SSepherosa Ziehau 	    &g_net_vsc_device_type) == 0) {
121315516c77SSepherosa Ziehau 		device_set_desc(dev, "Hyper-V Network Interface");
121415516c77SSepherosa Ziehau 		return BUS_PROBE_DEFAULT;
121515516c77SSepherosa Ziehau 	}
121615516c77SSepherosa Ziehau 	return ENXIO;
121715516c77SSepherosa Ziehau }
121815516c77SSepherosa Ziehau 
121915516c77SSepherosa Ziehau static int
122015516c77SSepherosa Ziehau hn_attach(device_t dev)
122115516c77SSepherosa Ziehau {
122215516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
122315516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
122415516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
122515516c77SSepherosa Ziehau 	uint8_t eaddr[ETHER_ADDR_LEN];
122615516c77SSepherosa Ziehau 	struct ifnet *ifp = NULL;
122715516c77SSepherosa Ziehau 	int error, ring_cnt, tx_ring_cnt;
122815516c77SSepherosa Ziehau 
122915516c77SSepherosa Ziehau 	sc->hn_dev = dev;
123015516c77SSepherosa Ziehau 	sc->hn_prichan = vmbus_get_channel(dev);
123115516c77SSepherosa Ziehau 	HN_LOCK_INIT(sc);
123215516c77SSepherosa Ziehau 
123315516c77SSepherosa Ziehau 	/*
1234dc13fee6SSepherosa Ziehau 	 * Initialize these tunables once.
1235dc13fee6SSepherosa Ziehau 	 */
1236dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = hn_tx_agg_size;
1237dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = hn_tx_agg_pkts;
1238dc13fee6SSepherosa Ziehau 
1239dc13fee6SSepherosa Ziehau 	/*
124015516c77SSepherosa Ziehau 	 * Setup taskqueue for transmission.
124115516c77SSepherosa Ziehau 	 */
12420e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_INDEP) {
1243fdd0222aSSepherosa Ziehau 		int i;
1244fdd0222aSSepherosa Ziehau 
1245fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs =
1246fdd0222aSSepherosa Ziehau 		    malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
1247fdd0222aSSepherosa Ziehau 		    M_DEVBUF, M_WAITOK);
1248fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i) {
1249fdd0222aSSepherosa Ziehau 			sc->hn_tx_taskqs[i] = taskqueue_create("hn_tx",
1250fdd0222aSSepherosa Ziehau 			    M_WAITOK, taskqueue_thread_enqueue,
1251fdd0222aSSepherosa Ziehau 			    &sc->hn_tx_taskqs[i]);
1252fdd0222aSSepherosa Ziehau 			taskqueue_start_threads(&sc->hn_tx_taskqs[i], 1, PI_NET,
1253fdd0222aSSepherosa Ziehau 			    "%s tx%d", device_get_nameunit(dev), i);
1254fdd0222aSSepherosa Ziehau 		}
12550e11868dSSepherosa Ziehau 	} else if (hn_tx_taskq_mode == HN_TX_TASKQ_M_GLOBAL) {
1256fdd0222aSSepherosa Ziehau 		sc->hn_tx_taskqs = hn_tx_taskque;
125715516c77SSepherosa Ziehau 	}
125815516c77SSepherosa Ziehau 
125915516c77SSepherosa Ziehau 	/*
126015516c77SSepherosa Ziehau 	 * Setup taskqueue for mangement tasks, e.g. link status.
126115516c77SSepherosa Ziehau 	 */
126215516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq0 = taskqueue_create("hn_mgmt", M_WAITOK,
126315516c77SSepherosa Ziehau 	    taskqueue_thread_enqueue, &sc->hn_mgmt_taskq0);
126415516c77SSepherosa Ziehau 	taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
126515516c77SSepherosa Ziehau 	    device_get_nameunit(dev));
126615516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc);
126715516c77SSepherosa Ziehau 	TASK_INIT(&sc->hn_netchg_init, 0, hn_netchg_init_taskfunc, sc);
126815516c77SSepherosa Ziehau 	TIMEOUT_TASK_INIT(sc->hn_mgmt_taskq0, &sc->hn_netchg_status, 0,
126915516c77SSepherosa Ziehau 	    hn_netchg_status_taskfunc, sc);
127015516c77SSepherosa Ziehau 
127115516c77SSepherosa Ziehau 	/*
127215516c77SSepherosa Ziehau 	 * Allocate ifnet and setup its name earlier, so that if_printf
127315516c77SSepherosa Ziehau 	 * can be used by functions, which will be called after
127415516c77SSepherosa Ziehau 	 * ether_ifattach().
127515516c77SSepherosa Ziehau 	 */
127615516c77SSepherosa Ziehau 	ifp = sc->hn_ifp = if_alloc(IFT_ETHER);
127715516c77SSepherosa Ziehau 	ifp->if_softc = sc;
127815516c77SSepherosa Ziehau 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
127915516c77SSepherosa Ziehau 
128015516c77SSepherosa Ziehau 	/*
128115516c77SSepherosa Ziehau 	 * Initialize ifmedia earlier so that it can be unconditionally
128215516c77SSepherosa Ziehau 	 * destroyed, if error happened later on.
128315516c77SSepherosa Ziehau 	 */
128415516c77SSepherosa Ziehau 	ifmedia_init(&sc->hn_media, 0, hn_ifmedia_upd, hn_ifmedia_sts);
128515516c77SSepherosa Ziehau 
128615516c77SSepherosa Ziehau 	/*
128715516c77SSepherosa Ziehau 	 * Figure out the # of RX rings (ring_cnt) and the # of TX rings
128815516c77SSepherosa Ziehau 	 * to use (tx_ring_cnt).
128915516c77SSepherosa Ziehau 	 *
129015516c77SSepherosa Ziehau 	 * NOTE:
129115516c77SSepherosa Ziehau 	 * The # of RX rings to use is same as the # of channels to use.
129215516c77SSepherosa Ziehau 	 */
129315516c77SSepherosa Ziehau 	ring_cnt = hn_chan_cnt;
129415516c77SSepherosa Ziehau 	if (ring_cnt <= 0) {
129515516c77SSepherosa Ziehau 		/* Default */
129615516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
129715516c77SSepherosa Ziehau 		if (ring_cnt > HN_RING_CNT_DEF_MAX)
129815516c77SSepherosa Ziehau 			ring_cnt = HN_RING_CNT_DEF_MAX;
129915516c77SSepherosa Ziehau 	} else if (ring_cnt > mp_ncpus) {
130015516c77SSepherosa Ziehau 		ring_cnt = mp_ncpus;
130115516c77SSepherosa Ziehau 	}
130234d68912SSepherosa Ziehau #ifdef RSS
130334d68912SSepherosa Ziehau 	if (ring_cnt > rss_getnumbuckets())
130434d68912SSepherosa Ziehau 		ring_cnt = rss_getnumbuckets();
130534d68912SSepherosa Ziehau #endif
130615516c77SSepherosa Ziehau 
130715516c77SSepherosa Ziehau 	tx_ring_cnt = hn_tx_ring_cnt;
130815516c77SSepherosa Ziehau 	if (tx_ring_cnt <= 0 || tx_ring_cnt > ring_cnt)
130915516c77SSepherosa Ziehau 		tx_ring_cnt = ring_cnt;
131023bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
131115516c77SSepherosa Ziehau 	if (hn_use_if_start) {
131215516c77SSepherosa Ziehau 		/* ifnet.if_start only needs one TX ring. */
131315516c77SSepherosa Ziehau 		tx_ring_cnt = 1;
131415516c77SSepherosa Ziehau 	}
131523bf9e15SSepherosa Ziehau #endif
131615516c77SSepherosa Ziehau 
131715516c77SSepherosa Ziehau 	/*
131815516c77SSepherosa Ziehau 	 * Set the leader CPU for channels.
131915516c77SSepherosa Ziehau 	 */
132015516c77SSepherosa Ziehau 	sc->hn_cpu = atomic_fetchadd_int(&hn_cpu_index, ring_cnt) % mp_ncpus;
132115516c77SSepherosa Ziehau 
132215516c77SSepherosa Ziehau 	/*
132315516c77SSepherosa Ziehau 	 * Create enough TX/RX rings, even if only limited number of
132415516c77SSepherosa Ziehau 	 * channels can be allocated.
132515516c77SSepherosa Ziehau 	 */
132615516c77SSepherosa Ziehau 	error = hn_create_tx_data(sc, tx_ring_cnt);
132715516c77SSepherosa Ziehau 	if (error)
132815516c77SSepherosa Ziehau 		goto failed;
132915516c77SSepherosa Ziehau 	error = hn_create_rx_data(sc, ring_cnt);
133015516c77SSepherosa Ziehau 	if (error)
133115516c77SSepherosa Ziehau 		goto failed;
133215516c77SSepherosa Ziehau 
133315516c77SSepherosa Ziehau 	/*
133415516c77SSepherosa Ziehau 	 * Create transaction context for NVS and RNDIS transactions.
133515516c77SSepherosa Ziehau 	 */
133615516c77SSepherosa Ziehau 	sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
133715516c77SSepherosa Ziehau 	    HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
133825641fc7SSepherosa Ziehau 	if (sc->hn_xact == NULL) {
133925641fc7SSepherosa Ziehau 		error = ENXIO;
134015516c77SSepherosa Ziehau 		goto failed;
134125641fc7SSepherosa Ziehau 	}
134225641fc7SSepherosa Ziehau 
134325641fc7SSepherosa Ziehau 	/*
134425641fc7SSepherosa Ziehau 	 * Install orphan handler for the revocation of this device's
134525641fc7SSepherosa Ziehau 	 * primary channel.
134625641fc7SSepherosa Ziehau 	 *
134725641fc7SSepherosa Ziehau 	 * NOTE:
134825641fc7SSepherosa Ziehau 	 * The processing order is critical here:
134925641fc7SSepherosa Ziehau 	 * Install the orphan handler, _before_ testing whether this
135025641fc7SSepherosa Ziehau 	 * device's primary channel has been revoked or not.
135125641fc7SSepherosa Ziehau 	 */
135225641fc7SSepherosa Ziehau 	vmbus_chan_set_orphan(sc->hn_prichan, sc->hn_xact);
135325641fc7SSepherosa Ziehau 	if (vmbus_chan_is_revoked(sc->hn_prichan)) {
135425641fc7SSepherosa Ziehau 		error = ENXIO;
135525641fc7SSepherosa Ziehau 		goto failed;
135625641fc7SSepherosa Ziehau 	}
135715516c77SSepherosa Ziehau 
135815516c77SSepherosa Ziehau 	/*
135915516c77SSepherosa Ziehau 	 * Attach the synthetic parts, i.e. NVS and RNDIS.
136015516c77SSepherosa Ziehau 	 */
136115516c77SSepherosa Ziehau 	error = hn_synth_attach(sc, ETHERMTU);
136215516c77SSepherosa Ziehau 	if (error)
136315516c77SSepherosa Ziehau 		goto failed;
136415516c77SSepherosa Ziehau 
136515516c77SSepherosa Ziehau 	error = hn_rndis_get_eaddr(sc, eaddr);
136615516c77SSepherosa Ziehau 	if (error)
136715516c77SSepherosa Ziehau 		goto failed;
136815516c77SSepherosa Ziehau 
136915516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
137015516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
137115516c77SSepherosa Ziehau 		/*
137215516c77SSepherosa Ziehau 		 * Reduce TCP segment aggregation limit for multiple
137315516c77SSepherosa Ziehau 		 * RX rings to increase ACK timeliness.
137415516c77SSepherosa Ziehau 		 */
137515516c77SSepherosa Ziehau 		hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MULTIRX_DEF);
137615516c77SSepherosa Ziehau 	}
137715516c77SSepherosa Ziehau #endif
137815516c77SSepherosa Ziehau 
137915516c77SSepherosa Ziehau 	/*
138015516c77SSepherosa Ziehau 	 * Fixup TX stuffs after synthetic parts are attached.
138115516c77SSepherosa Ziehau 	 */
138215516c77SSepherosa Ziehau 	hn_fixup_tx_data(sc);
138315516c77SSepherosa Ziehau 
138415516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
138515516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
138615516c77SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "nvs_version", CTLFLAG_RD,
138715516c77SSepherosa Ziehau 	    &sc->hn_nvs_ver, 0, "NVS version");
138815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "ndis_version",
138915516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
139015516c77SSepherosa Ziehau 	    hn_ndis_version_sysctl, "A", "NDIS version");
139115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "caps",
139215516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
139315516c77SSepherosa Ziehau 	    hn_caps_sysctl, "A", "capabilities");
139415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist",
139515516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
139615516c77SSepherosa Ziehau 	    hn_hwassist_sysctl, "A", "hwassist");
139715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter",
139815516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
139915516c77SSepherosa Ziehau 	    hn_rxfilter_sysctl, "A", "rxfilter");
140015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hash",
140115516c77SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
140215516c77SSepherosa Ziehau 	    hn_rss_hash_sysctl, "A", "RSS hash");
140315516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rss_ind_size",
140415516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rss_ind_size, 0, "RSS indirect entry count");
140534d68912SSepherosa Ziehau #ifndef RSS
140634d68912SSepherosa Ziehau 	/*
140734d68912SSepherosa Ziehau 	 * Don't allow RSS key/indirect table changes, if RSS is defined.
140834d68912SSepherosa Ziehau 	 */
140915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key",
141015516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
141115516c77SSepherosa Ziehau 	    hn_rss_key_sysctl, "IU", "RSS key");
141215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_ind",
141315516c77SSepherosa Ziehau 	    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
141415516c77SSepherosa Ziehau 	    hn_rss_ind_sysctl, "IU", "RSS indirect table");
141534d68912SSepherosa Ziehau #endif
1416dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_size",
1417dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_size, 0,
1418dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation size limit");
1419dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_pkts",
1420dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_pkts, 0,
1421dc13fee6SSepherosa Ziehau 	    "RNDIS offered packet transmission aggregation count limit");
1422dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rndis_agg_align",
1423dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rndis_agg_align, 0,
1424dc13fee6SSepherosa Ziehau 	    "RNDIS packet transmission aggregation alignment");
1425dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_size",
1426dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
1427dc13fee6SSepherosa Ziehau 	    hn_txagg_size_sysctl, "I",
1428dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation size, 0 -- disable, -1 -- auto");
1429dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pkts",
1430dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
1431dc13fee6SSepherosa Ziehau 	    hn_txagg_pkts_sysctl, "I",
1432dc13fee6SSepherosa Ziehau 	    "Packet transmission aggregation packets, "
1433dc13fee6SSepherosa Ziehau 	    "0 -- disable, -1 -- auto");
14346c1204dfSSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "polling",
14356c1204dfSSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
14366c1204dfSSepherosa Ziehau 	    hn_polling_sysctl, "I",
14376c1204dfSSepherosa Ziehau 	    "Polling frequency: [100,1000000], 0 disable polling");
143840d60d6eSDexuan Cui 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "vf",
143940d60d6eSDexuan Cui 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
144040d60d6eSDexuan Cui 	    hn_vf_sysctl, "A", "Virtual Function's name");
1441499c3e17SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxvf",
1442499c3e17SSepherosa Ziehau 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
1443499c3e17SSepherosa Ziehau 	    hn_rxvf_sysctl, "A", "activated Virtual Function's name");
144415516c77SSepherosa Ziehau 
144515516c77SSepherosa Ziehau 	/*
144615516c77SSepherosa Ziehau 	 * Setup the ifmedia, which has been initialized earlier.
144715516c77SSepherosa Ziehau 	 */
144815516c77SSepherosa Ziehau 	ifmedia_add(&sc->hn_media, IFM_ETHER | IFM_AUTO, 0, NULL);
144915516c77SSepherosa Ziehau 	ifmedia_set(&sc->hn_media, IFM_ETHER | IFM_AUTO);
145015516c77SSepherosa Ziehau 	/* XXX ifmedia_set really should do this for us */
145115516c77SSepherosa Ziehau 	sc->hn_media.ifm_media = sc->hn_media.ifm_cur->ifm_media;
145215516c77SSepherosa Ziehau 
145315516c77SSepherosa Ziehau 	/*
145415516c77SSepherosa Ziehau 	 * Setup the ifnet for this interface.
145515516c77SSepherosa Ziehau 	 */
145615516c77SSepherosa Ziehau 
145715516c77SSepherosa Ziehau 	ifp->if_baudrate = IF_Gbps(10);
145815516c77SSepherosa Ziehau 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
145915516c77SSepherosa Ziehau 	ifp->if_ioctl = hn_ioctl;
146015516c77SSepherosa Ziehau 	ifp->if_init = hn_init;
146123bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
146215516c77SSepherosa Ziehau 	if (hn_use_if_start) {
146315516c77SSepherosa Ziehau 		int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]);
146415516c77SSepherosa Ziehau 
146515516c77SSepherosa Ziehau 		ifp->if_start = hn_start;
146615516c77SSepherosa Ziehau 		IFQ_SET_MAXLEN(&ifp->if_snd, qdepth);
146715516c77SSepherosa Ziehau 		ifp->if_snd.ifq_drv_maxlen = qdepth - 1;
146815516c77SSepherosa Ziehau 		IFQ_SET_READY(&ifp->if_snd);
146923bf9e15SSepherosa Ziehau 	} else
147023bf9e15SSepherosa Ziehau #endif
147123bf9e15SSepherosa Ziehau 	{
147215516c77SSepherosa Ziehau 		ifp->if_transmit = hn_transmit;
147315516c77SSepherosa Ziehau 		ifp->if_qflush = hn_xmit_qflush;
147415516c77SSepherosa Ziehau 	}
147515516c77SSepherosa Ziehau 
147615516c77SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_LRO;
147715516c77SSepherosa Ziehau #ifdef foo
147815516c77SSepherosa Ziehau 	/* We can't diff IPv6 packets from IPv4 packets on RX path. */
147915516c77SSepherosa Ziehau 	ifp->if_capabilities |= IFCAP_RXCSUM_IPV6;
148015516c77SSepherosa Ziehau #endif
148115516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_VLAN) {
148215516c77SSepherosa Ziehau 		/* XXX not sure about VLAN_MTU. */
148315516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
148415516c77SSepherosa Ziehau 	}
148515516c77SSepherosa Ziehau 
148615516c77SSepherosa Ziehau 	ifp->if_hwassist = sc->hn_tx_ring[0].hn_csum_assist;
148715516c77SSepherosa Ziehau 	if (ifp->if_hwassist & HN_CSUM_IP_MASK)
148815516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TXCSUM;
148915516c77SSepherosa Ziehau 	if (ifp->if_hwassist & HN_CSUM_IP6_MASK)
149015516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TXCSUM_IPV6;
149115516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO4) {
149215516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TSO4;
149315516c77SSepherosa Ziehau 		ifp->if_hwassist |= CSUM_IP_TSO;
149415516c77SSepherosa Ziehau 	}
149515516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TSO6) {
149615516c77SSepherosa Ziehau 		ifp->if_capabilities |= IFCAP_TSO6;
149715516c77SSepherosa Ziehau 		ifp->if_hwassist |= CSUM_IP6_TSO;
149815516c77SSepherosa Ziehau 	}
149915516c77SSepherosa Ziehau 
150015516c77SSepherosa Ziehau 	/* Enable all available capabilities by default. */
150115516c77SSepherosa Ziehau 	ifp->if_capenable = ifp->if_capabilities;
150215516c77SSepherosa Ziehau 
15037960e6baSSepherosa Ziehau 	/*
15047960e6baSSepherosa Ziehau 	 * Disable IPv6 TSO and TXCSUM by default, they still can
15057960e6baSSepherosa Ziehau 	 * be enabled through SIOCSIFCAP.
15067960e6baSSepherosa Ziehau 	 */
15077960e6baSSepherosa Ziehau 	ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6 | IFCAP_TSO6);
15087960e6baSSepherosa Ziehau 	ifp->if_hwassist &= ~(HN_CSUM_IP6_MASK | CSUM_IP6_TSO);
15097960e6baSSepherosa Ziehau 
151015516c77SSepherosa Ziehau 	if (ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) {
151115516c77SSepherosa Ziehau 		hn_set_tso_maxsize(sc, hn_tso_maxlen, ETHERMTU);
151215516c77SSepherosa Ziehau 		ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX;
151315516c77SSepherosa Ziehau 		ifp->if_hw_tsomaxsegsize = PAGE_SIZE;
151415516c77SSepherosa Ziehau 	}
151515516c77SSepherosa Ziehau 
151615516c77SSepherosa Ziehau 	ether_ifattach(ifp, eaddr);
151715516c77SSepherosa Ziehau 
151815516c77SSepherosa Ziehau 	if ((ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) && bootverbose) {
151915516c77SSepherosa Ziehau 		if_printf(ifp, "TSO segcnt %u segsz %u\n",
152015516c77SSepherosa Ziehau 		    ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize);
152115516c77SSepherosa Ziehau 	}
152215516c77SSepherosa Ziehau 
152315516c77SSepherosa Ziehau 	/* Inform the upper layer about the long frame support. */
152415516c77SSepherosa Ziehau 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
152515516c77SSepherosa Ziehau 
152615516c77SSepherosa Ziehau 	/*
152715516c77SSepherosa Ziehau 	 * Kick off link status check.
152815516c77SSepherosa Ziehau 	 */
152915516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
153015516c77SSepherosa Ziehau 	hn_update_link_status(sc);
153115516c77SSepherosa Ziehau 
15325bdfd3fdSDexuan Cui 	sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event,
15335bdfd3fdSDexuan Cui 	    hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY);
15345bdfd3fdSDexuan Cui 	sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event,
15355bdfd3fdSDexuan Cui 	    hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY);
15365bdfd3fdSDexuan Cui 
1537499c3e17SSepherosa Ziehau 	sc->hn_ifnet_atthand = EVENTHANDLER_REGISTER(ether_ifattach_event,
1538499c3e17SSepherosa Ziehau 	    hn_ifnet_attevent, sc, EVENTHANDLER_PRI_ANY);
1539499c3e17SSepherosa Ziehau 	sc->hn_ifnet_dethand = EVENTHANDLER_REGISTER(ifnet_departure_event,
1540499c3e17SSepherosa Ziehau 	    hn_ifnet_detevent, sc, EVENTHANDLER_PRI_ANY);
1541499c3e17SSepherosa Ziehau 
154215516c77SSepherosa Ziehau 	return (0);
154315516c77SSepherosa Ziehau failed:
154415516c77SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
154515516c77SSepherosa Ziehau 		hn_synth_detach(sc);
154615516c77SSepherosa Ziehau 	hn_detach(dev);
154715516c77SSepherosa Ziehau 	return (error);
154815516c77SSepherosa Ziehau }
154915516c77SSepherosa Ziehau 
155015516c77SSepherosa Ziehau static int
155115516c77SSepherosa Ziehau hn_detach(device_t dev)
155215516c77SSepherosa Ziehau {
155315516c77SSepherosa Ziehau 	struct hn_softc *sc = device_get_softc(dev);
1554499c3e17SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp, *vf_ifp;
155515516c77SSepherosa Ziehau 
15565bdfd3fdSDexuan Cui 	if (sc->hn_ifaddr_evthand != NULL)
15575bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand);
15585bdfd3fdSDexuan Cui 	if (sc->hn_ifnet_evthand != NULL)
15595bdfd3fdSDexuan Cui 		EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand);
1560499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_atthand != NULL) {
1561499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ether_ifattach_event,
1562499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_atthand);
1563499c3e17SSepherosa Ziehau 	}
1564499c3e17SSepherosa Ziehau 	if (sc->hn_ifnet_dethand != NULL) {
1565499c3e17SSepherosa Ziehau 		EVENTHANDLER_DEREGISTER(ifnet_departure_event,
1566499c3e17SSepherosa Ziehau 		    sc->hn_ifnet_dethand);
1567499c3e17SSepherosa Ziehau 	}
1568499c3e17SSepherosa Ziehau 
1569499c3e17SSepherosa Ziehau 	vf_ifp = sc->hn_vf_ifp;
1570499c3e17SSepherosa Ziehau 	__compiler_membar();
1571499c3e17SSepherosa Ziehau 	if (vf_ifp != NULL)
1572499c3e17SSepherosa Ziehau 		hn_ifnet_detevent(sc, vf_ifp);
15735bdfd3fdSDexuan Cui 
157425641fc7SSepherosa Ziehau 	if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
157525641fc7SSepherosa Ziehau 		/*
157625641fc7SSepherosa Ziehau 		 * In case that the vmbus missed the orphan handler
157725641fc7SSepherosa Ziehau 		 * installation.
157825641fc7SSepherosa Ziehau 		 */
157925641fc7SSepherosa Ziehau 		vmbus_xact_ctx_orphan(sc->hn_xact);
158025641fc7SSepherosa Ziehau 	}
158125641fc7SSepherosa Ziehau 
158215516c77SSepherosa Ziehau 	if (device_is_attached(dev)) {
158315516c77SSepherosa Ziehau 		HN_LOCK(sc);
158415516c77SSepherosa Ziehau 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
158515516c77SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
15865bdfd3fdSDexuan Cui 				hn_stop(sc, true);
158715516c77SSepherosa Ziehau 			/*
158815516c77SSepherosa Ziehau 			 * NOTE:
158915516c77SSepherosa Ziehau 			 * hn_stop() only suspends data, so managment
159015516c77SSepherosa Ziehau 			 * stuffs have to be suspended manually here.
159115516c77SSepherosa Ziehau 			 */
159215516c77SSepherosa Ziehau 			hn_suspend_mgmt(sc);
159315516c77SSepherosa Ziehau 			hn_synth_detach(sc);
159415516c77SSepherosa Ziehau 		}
159515516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
159615516c77SSepherosa Ziehau 		ether_ifdetach(ifp);
159715516c77SSepherosa Ziehau 	}
159815516c77SSepherosa Ziehau 
159915516c77SSepherosa Ziehau 	ifmedia_removeall(&sc->hn_media);
160015516c77SSepherosa Ziehau 	hn_destroy_rx_data(sc);
160115516c77SSepherosa Ziehau 	hn_destroy_tx_data(sc);
160215516c77SSepherosa Ziehau 
16030e11868dSSepherosa Ziehau 	if (sc->hn_tx_taskqs != NULL && sc->hn_tx_taskqs != hn_tx_taskque) {
1604fdd0222aSSepherosa Ziehau 		int i;
1605fdd0222aSSepherosa Ziehau 
1606fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
1607fdd0222aSSepherosa Ziehau 			taskqueue_free(sc->hn_tx_taskqs[i]);
1608fdd0222aSSepherosa Ziehau 		free(sc->hn_tx_taskqs, M_DEVBUF);
1609fdd0222aSSepherosa Ziehau 	}
161015516c77SSepherosa Ziehau 	taskqueue_free(sc->hn_mgmt_taskq0);
161115516c77SSepherosa Ziehau 
161225641fc7SSepherosa Ziehau 	if (sc->hn_xact != NULL) {
161325641fc7SSepherosa Ziehau 		/*
161425641fc7SSepherosa Ziehau 		 * Uninstall the orphan handler _before_ the xact is
161525641fc7SSepherosa Ziehau 		 * destructed.
161625641fc7SSepherosa Ziehau 		 */
161725641fc7SSepherosa Ziehau 		vmbus_chan_unset_orphan(sc->hn_prichan);
161815516c77SSepherosa Ziehau 		vmbus_xact_ctx_destroy(sc->hn_xact);
161925641fc7SSepherosa Ziehau 	}
162015516c77SSepherosa Ziehau 
162115516c77SSepherosa Ziehau 	if_free(ifp);
162215516c77SSepherosa Ziehau 
162315516c77SSepherosa Ziehau 	HN_LOCK_DESTROY(sc);
162415516c77SSepherosa Ziehau 	return (0);
162515516c77SSepherosa Ziehau }
162615516c77SSepherosa Ziehau 
162715516c77SSepherosa Ziehau static int
162815516c77SSepherosa Ziehau hn_shutdown(device_t dev)
162915516c77SSepherosa Ziehau {
163015516c77SSepherosa Ziehau 
163115516c77SSepherosa Ziehau 	return (0);
163215516c77SSepherosa Ziehau }
163315516c77SSepherosa Ziehau 
163415516c77SSepherosa Ziehau static void
163515516c77SSepherosa Ziehau hn_link_status(struct hn_softc *sc)
163615516c77SSepherosa Ziehau {
163715516c77SSepherosa Ziehau 	uint32_t link_status;
163815516c77SSepherosa Ziehau 	int error;
163915516c77SSepherosa Ziehau 
164015516c77SSepherosa Ziehau 	error = hn_rndis_get_linkstatus(sc, &link_status);
164115516c77SSepherosa Ziehau 	if (error) {
164215516c77SSepherosa Ziehau 		/* XXX what to do? */
164315516c77SSepherosa Ziehau 		return;
164415516c77SSepherosa Ziehau 	}
164515516c77SSepherosa Ziehau 
164615516c77SSepherosa Ziehau 	if (link_status == NDIS_MEDIA_STATE_CONNECTED)
164715516c77SSepherosa Ziehau 		sc->hn_link_flags |= HN_LINK_FLAG_LINKUP;
164815516c77SSepherosa Ziehau 	else
164915516c77SSepherosa Ziehau 		sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
165015516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp,
165115516c77SSepherosa Ziehau 	    (sc->hn_link_flags & HN_LINK_FLAG_LINKUP) ?
165215516c77SSepherosa Ziehau 	    LINK_STATE_UP : LINK_STATE_DOWN);
165315516c77SSepherosa Ziehau }
165415516c77SSepherosa Ziehau 
165515516c77SSepherosa Ziehau static void
165615516c77SSepherosa Ziehau hn_link_taskfunc(void *xsc, int pending __unused)
165715516c77SSepherosa Ziehau {
165815516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
165915516c77SSepherosa Ziehau 
166015516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
166115516c77SSepherosa Ziehau 		return;
166215516c77SSepherosa Ziehau 	hn_link_status(sc);
166315516c77SSepherosa Ziehau }
166415516c77SSepherosa Ziehau 
166515516c77SSepherosa Ziehau static void
166615516c77SSepherosa Ziehau hn_netchg_init_taskfunc(void *xsc, int pending __unused)
166715516c77SSepherosa Ziehau {
166815516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
166915516c77SSepherosa Ziehau 
167015516c77SSepherosa Ziehau 	/* Prevent any link status checks from running. */
167115516c77SSepherosa Ziehau 	sc->hn_link_flags |= HN_LINK_FLAG_NETCHG;
167215516c77SSepherosa Ziehau 
167315516c77SSepherosa Ziehau 	/*
167415516c77SSepherosa Ziehau 	 * Fake up a [link down --> link up] state change; 5 seconds
167515516c77SSepherosa Ziehau 	 * delay is used, which closely simulates miibus reaction
167615516c77SSepherosa Ziehau 	 * upon link down event.
167715516c77SSepherosa Ziehau 	 */
167815516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
167915516c77SSepherosa Ziehau 	if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
168015516c77SSepherosa Ziehau 	taskqueue_enqueue_timeout(sc->hn_mgmt_taskq0,
168115516c77SSepherosa Ziehau 	    &sc->hn_netchg_status, 5 * hz);
168215516c77SSepherosa Ziehau }
168315516c77SSepherosa Ziehau 
168415516c77SSepherosa Ziehau static void
168515516c77SSepherosa Ziehau hn_netchg_status_taskfunc(void *xsc, int pending __unused)
168615516c77SSepherosa Ziehau {
168715516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
168815516c77SSepherosa Ziehau 
168915516c77SSepherosa Ziehau 	/* Re-allow link status checks. */
169015516c77SSepherosa Ziehau 	sc->hn_link_flags &= ~HN_LINK_FLAG_NETCHG;
169115516c77SSepherosa Ziehau 	hn_link_status(sc);
169215516c77SSepherosa Ziehau }
169315516c77SSepherosa Ziehau 
169415516c77SSepherosa Ziehau static void
169515516c77SSepherosa Ziehau hn_update_link_status(struct hn_softc *sc)
169615516c77SSepherosa Ziehau {
169715516c77SSepherosa Ziehau 
169815516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
169915516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
170015516c77SSepherosa Ziehau }
170115516c77SSepherosa Ziehau 
170215516c77SSepherosa Ziehau static void
170315516c77SSepherosa Ziehau hn_change_network(struct hn_softc *sc)
170415516c77SSepherosa Ziehau {
170515516c77SSepherosa Ziehau 
170615516c77SSepherosa Ziehau 	if (sc->hn_mgmt_taskq != NULL)
170715516c77SSepherosa Ziehau 		taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_netchg_init);
170815516c77SSepherosa Ziehau }
170915516c77SSepherosa Ziehau 
171015516c77SSepherosa Ziehau static __inline int
171115516c77SSepherosa Ziehau hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
171215516c77SSepherosa Ziehau     struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
171315516c77SSepherosa Ziehau {
171415516c77SSepherosa Ziehau 	struct mbuf *m = *m_head;
171515516c77SSepherosa Ziehau 	int error;
171615516c77SSepherosa Ziehau 
171715516c77SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID, ("txd uses chim"));
171815516c77SSepherosa Ziehau 
171915516c77SSepherosa Ziehau 	error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag, txd->data_dmap,
172015516c77SSepherosa Ziehau 	    m, segs, nsegs, BUS_DMA_NOWAIT);
172115516c77SSepherosa Ziehau 	if (error == EFBIG) {
172215516c77SSepherosa Ziehau 		struct mbuf *m_new;
172315516c77SSepherosa Ziehau 
172415516c77SSepherosa Ziehau 		m_new = m_collapse(m, M_NOWAIT, HN_TX_DATA_SEGCNT_MAX);
172515516c77SSepherosa Ziehau 		if (m_new == NULL)
172615516c77SSepherosa Ziehau 			return ENOBUFS;
172715516c77SSepherosa Ziehau 		else
172815516c77SSepherosa Ziehau 			*m_head = m = m_new;
172915516c77SSepherosa Ziehau 		txr->hn_tx_collapsed++;
173015516c77SSepherosa Ziehau 
173115516c77SSepherosa Ziehau 		error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag,
173215516c77SSepherosa Ziehau 		    txd->data_dmap, m, segs, nsegs, BUS_DMA_NOWAIT);
173315516c77SSepherosa Ziehau 	}
173415516c77SSepherosa Ziehau 	if (!error) {
173515516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag, txd->data_dmap,
173615516c77SSepherosa Ziehau 		    BUS_DMASYNC_PREWRITE);
173715516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_DMAMAP;
173815516c77SSepherosa Ziehau 	}
173915516c77SSepherosa Ziehau 	return error;
174015516c77SSepherosa Ziehau }
174115516c77SSepherosa Ziehau 
174215516c77SSepherosa Ziehau static __inline int
174315516c77SSepherosa Ziehau hn_txdesc_put(struct hn_tx_ring *txr, struct hn_txdesc *txd)
174415516c77SSepherosa Ziehau {
174515516c77SSepherosa Ziehau 
174615516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONLIST) == 0,
174715516c77SSepherosa Ziehau 	    ("put an onlist txd %#x", txd->flags));
1748dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
1749dc13fee6SSepherosa Ziehau 	    ("put an onagg txd %#x", txd->flags));
175015516c77SSepherosa Ziehau 
175115516c77SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
175215516c77SSepherosa Ziehau 	if (atomic_fetchadd_int(&txd->refs, -1) != 1)
175315516c77SSepherosa Ziehau 		return 0;
175415516c77SSepherosa Ziehau 
1755dc13fee6SSepherosa Ziehau 	if (!STAILQ_EMPTY(&txd->agg_list)) {
1756dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tmp_txd;
1757dc13fee6SSepherosa Ziehau 
1758dc13fee6SSepherosa Ziehau 		while ((tmp_txd = STAILQ_FIRST(&txd->agg_list)) != NULL) {
1759dc13fee6SSepherosa Ziehau 			int freed;
1760dc13fee6SSepherosa Ziehau 
1761dc13fee6SSepherosa Ziehau 			KASSERT(STAILQ_EMPTY(&tmp_txd->agg_list),
1762dc13fee6SSepherosa Ziehau 			    ("resursive aggregation on aggregated txdesc"));
1763dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_ONAGG),
1764dc13fee6SSepherosa Ziehau 			    ("not aggregated txdesc"));
1765dc13fee6SSepherosa Ziehau 			KASSERT((tmp_txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
1766dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc uses dmamap"));
1767dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
1768dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc consumes "
1769dc13fee6SSepherosa Ziehau 			     "chimney sending buffer"));
1770dc13fee6SSepherosa Ziehau 			KASSERT(tmp_txd->chim_size == 0,
1771dc13fee6SSepherosa Ziehau 			    ("aggregated txdesc has non-zero "
1772dc13fee6SSepherosa Ziehau 			     "chimney sending size"));
1773dc13fee6SSepherosa Ziehau 
1774dc13fee6SSepherosa Ziehau 			STAILQ_REMOVE_HEAD(&txd->agg_list, agg_link);
1775dc13fee6SSepherosa Ziehau 			tmp_txd->flags &= ~HN_TXD_FLAG_ONAGG;
1776dc13fee6SSepherosa Ziehau 			freed = hn_txdesc_put(txr, tmp_txd);
1777dc13fee6SSepherosa Ziehau 			KASSERT(freed, ("failed to free aggregated txdesc"));
1778dc13fee6SSepherosa Ziehau 		}
1779dc13fee6SSepherosa Ziehau 	}
1780dc13fee6SSepherosa Ziehau 
178115516c77SSepherosa Ziehau 	if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) {
178215516c77SSepherosa Ziehau 		KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
178315516c77SSepherosa Ziehau 		    ("chim txd uses dmamap"));
178415516c77SSepherosa Ziehau 		hn_chim_free(txr->hn_sc, txd->chim_index);
178515516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
1786dc13fee6SSepherosa Ziehau 		txd->chim_size = 0;
178715516c77SSepherosa Ziehau 	} else if (txd->flags & HN_TXD_FLAG_DMAMAP) {
178815516c77SSepherosa Ziehau 		bus_dmamap_sync(txr->hn_tx_data_dtag,
178915516c77SSepherosa Ziehau 		    txd->data_dmap, BUS_DMASYNC_POSTWRITE);
179015516c77SSepherosa Ziehau 		bus_dmamap_unload(txr->hn_tx_data_dtag,
179115516c77SSepherosa Ziehau 		    txd->data_dmap);
179215516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_DMAMAP;
179315516c77SSepherosa Ziehau 	}
179415516c77SSepherosa Ziehau 
179515516c77SSepherosa Ziehau 	if (txd->m != NULL) {
179615516c77SSepherosa Ziehau 		m_freem(txd->m);
179715516c77SSepherosa Ziehau 		txd->m = NULL;
179815516c77SSepherosa Ziehau 	}
179915516c77SSepherosa Ziehau 
180015516c77SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONLIST;
180115516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
180215516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
180315516c77SSepherosa Ziehau 	KASSERT(txr->hn_txdesc_avail >= 0 &&
180415516c77SSepherosa Ziehau 	    txr->hn_txdesc_avail < txr->hn_txdesc_cnt,
180515516c77SSepherosa Ziehau 	    ("txdesc_put: invalid txd avail %d", txr->hn_txdesc_avail));
180615516c77SSepherosa Ziehau 	txr->hn_txdesc_avail++;
180715516c77SSepherosa Ziehau 	SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
180815516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
180985e4ae1eSSepherosa Ziehau #else	/* HN_USE_TXDESC_BUFRING */
181085e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
181115516c77SSepherosa Ziehau 	atomic_add_int(&txr->hn_txdesc_avail, 1);
181215516c77SSepherosa Ziehau #endif
181385e4ae1eSSepherosa Ziehau 	buf_ring_enqueue(txr->hn_txdesc_br, txd);
181485e4ae1eSSepherosa Ziehau #endif	/* !HN_USE_TXDESC_BUFRING */
181515516c77SSepherosa Ziehau 
181615516c77SSepherosa Ziehau 	return 1;
181715516c77SSepherosa Ziehau }
181815516c77SSepherosa Ziehau 
181915516c77SSepherosa Ziehau static __inline struct hn_txdesc *
182015516c77SSepherosa Ziehau hn_txdesc_get(struct hn_tx_ring *txr)
182115516c77SSepherosa Ziehau {
182215516c77SSepherosa Ziehau 	struct hn_txdesc *txd;
182315516c77SSepherosa Ziehau 
182415516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
182515516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
182615516c77SSepherosa Ziehau 	txd = SLIST_FIRST(&txr->hn_txlist);
182715516c77SSepherosa Ziehau 	if (txd != NULL) {
182815516c77SSepherosa Ziehau 		KASSERT(txr->hn_txdesc_avail > 0,
182915516c77SSepherosa Ziehau 		    ("txdesc_get: invalid txd avail %d", txr->hn_txdesc_avail));
183015516c77SSepherosa Ziehau 		txr->hn_txdesc_avail--;
183115516c77SSepherosa Ziehau 		SLIST_REMOVE_HEAD(&txr->hn_txlist, link);
183215516c77SSepherosa Ziehau 	}
183315516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
183415516c77SSepherosa Ziehau #else
183515516c77SSepherosa Ziehau 	txd = buf_ring_dequeue_sc(txr->hn_txdesc_br);
183615516c77SSepherosa Ziehau #endif
183715516c77SSepherosa Ziehau 
183815516c77SSepherosa Ziehau 	if (txd != NULL) {
183915516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
184085e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
184115516c77SSepherosa Ziehau 		atomic_subtract_int(&txr->hn_txdesc_avail, 1);
184215516c77SSepherosa Ziehau #endif
184385e4ae1eSSepherosa Ziehau #endif	/* HN_USE_TXDESC_BUFRING */
184415516c77SSepherosa Ziehau 		KASSERT(txd->m == NULL && txd->refs == 0 &&
1845dc13fee6SSepherosa Ziehau 		    STAILQ_EMPTY(&txd->agg_list) &&
184615516c77SSepherosa Ziehau 		    txd->chim_index == HN_NVS_CHIM_IDX_INVALID &&
1847dc13fee6SSepherosa Ziehau 		    txd->chim_size == 0 &&
184815516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONLIST) &&
1849dc13fee6SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_ONAGG) == 0 &&
185015516c77SSepherosa Ziehau 		    (txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("invalid txd"));
185115516c77SSepherosa Ziehau 		txd->flags &= ~HN_TXD_FLAG_ONLIST;
185215516c77SSepherosa Ziehau 		txd->refs = 1;
185315516c77SSepherosa Ziehau 	}
185415516c77SSepherosa Ziehau 	return txd;
185515516c77SSepherosa Ziehau }
185615516c77SSepherosa Ziehau 
185715516c77SSepherosa Ziehau static __inline void
185815516c77SSepherosa Ziehau hn_txdesc_hold(struct hn_txdesc *txd)
185915516c77SSepherosa Ziehau {
186015516c77SSepherosa Ziehau 
186115516c77SSepherosa Ziehau 	/* 0->1 transition will never work */
186225641fc7SSepherosa Ziehau 	KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
186315516c77SSepherosa Ziehau 	atomic_add_int(&txd->refs, 1);
186415516c77SSepherosa Ziehau }
186515516c77SSepherosa Ziehau 
1866dc13fee6SSepherosa Ziehau static __inline void
1867dc13fee6SSepherosa Ziehau hn_txdesc_agg(struct hn_txdesc *agg_txd, struct hn_txdesc *txd)
1868dc13fee6SSepherosa Ziehau {
1869dc13fee6SSepherosa Ziehau 
1870dc13fee6SSepherosa Ziehau 	KASSERT((agg_txd->flags & HN_TXD_FLAG_ONAGG) == 0,
1871dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on aggregating txdesc"));
1872dc13fee6SSepherosa Ziehau 
1873dc13fee6SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_ONAGG) == 0,
1874dc13fee6SSepherosa Ziehau 	    ("already aggregated"));
1875dc13fee6SSepherosa Ziehau 	KASSERT(STAILQ_EMPTY(&txd->agg_list),
1876dc13fee6SSepherosa Ziehau 	    ("recursive aggregation on to-be-aggregated txdesc"));
1877dc13fee6SSepherosa Ziehau 
1878dc13fee6SSepherosa Ziehau 	txd->flags |= HN_TXD_FLAG_ONAGG;
1879dc13fee6SSepherosa Ziehau 	STAILQ_INSERT_TAIL(&agg_txd->agg_list, txd, agg_link);
1880dc13fee6SSepherosa Ziehau }
1881dc13fee6SSepherosa Ziehau 
188215516c77SSepherosa Ziehau static bool
188315516c77SSepherosa Ziehau hn_tx_ring_pending(struct hn_tx_ring *txr)
188415516c77SSepherosa Ziehau {
188515516c77SSepherosa Ziehau 	bool pending = false;
188615516c77SSepherosa Ziehau 
188715516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
188815516c77SSepherosa Ziehau 	mtx_lock_spin(&txr->hn_txlist_spin);
188915516c77SSepherosa Ziehau 	if (txr->hn_txdesc_avail != txr->hn_txdesc_cnt)
189015516c77SSepherosa Ziehau 		pending = true;
189115516c77SSepherosa Ziehau 	mtx_unlock_spin(&txr->hn_txlist_spin);
189215516c77SSepherosa Ziehau #else
189315516c77SSepherosa Ziehau 	if (!buf_ring_full(txr->hn_txdesc_br))
189415516c77SSepherosa Ziehau 		pending = true;
189515516c77SSepherosa Ziehau #endif
189615516c77SSepherosa Ziehau 	return (pending);
189715516c77SSepherosa Ziehau }
189815516c77SSepherosa Ziehau 
189915516c77SSepherosa Ziehau static __inline void
190015516c77SSepherosa Ziehau hn_txeof(struct hn_tx_ring *txr)
190115516c77SSepherosa Ziehau {
190215516c77SSepherosa Ziehau 	txr->hn_has_txeof = 0;
190315516c77SSepherosa Ziehau 	txr->hn_txeof(txr);
190415516c77SSepherosa Ziehau }
190515516c77SSepherosa Ziehau 
190615516c77SSepherosa Ziehau static void
190715516c77SSepherosa Ziehau hn_txpkt_done(struct hn_nvs_sendctx *sndc, struct hn_softc *sc,
190815516c77SSepherosa Ziehau     struct vmbus_channel *chan, const void *data __unused, int dlen __unused)
190915516c77SSepherosa Ziehau {
191015516c77SSepherosa Ziehau 	struct hn_txdesc *txd = sndc->hn_cbarg;
191115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
191215516c77SSepherosa Ziehau 
191315516c77SSepherosa Ziehau 	txr = txd->txr;
191415516c77SSepherosa Ziehau 	KASSERT(txr->hn_chan == chan,
191515516c77SSepherosa Ziehau 	    ("channel mismatch, on chan%u, should be chan%u",
1916aa1a2adcSSepherosa Ziehau 	     vmbus_chan_id(chan), vmbus_chan_id(txr->hn_chan)));
191715516c77SSepherosa Ziehau 
191815516c77SSepherosa Ziehau 	txr->hn_has_txeof = 1;
191915516c77SSepherosa Ziehau 	hn_txdesc_put(txr, txd);
192015516c77SSepherosa Ziehau 
192115516c77SSepherosa Ziehau 	++txr->hn_txdone_cnt;
192215516c77SSepherosa Ziehau 	if (txr->hn_txdone_cnt >= HN_EARLY_TXEOF_THRESH) {
192315516c77SSepherosa Ziehau 		txr->hn_txdone_cnt = 0;
192415516c77SSepherosa Ziehau 		if (txr->hn_oactive)
192515516c77SSepherosa Ziehau 			hn_txeof(txr);
192615516c77SSepherosa Ziehau 	}
192715516c77SSepherosa Ziehau }
192815516c77SSepherosa Ziehau 
192915516c77SSepherosa Ziehau static void
193015516c77SSepherosa Ziehau hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
193115516c77SSepherosa Ziehau {
193215516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
193315516c77SSepherosa Ziehau 	tcp_lro_flush_all(&rxr->hn_lro);
193415516c77SSepherosa Ziehau #endif
193515516c77SSepherosa Ziehau 
193615516c77SSepherosa Ziehau 	/*
193715516c77SSepherosa Ziehau 	 * NOTE:
193815516c77SSepherosa Ziehau 	 * 'txr' could be NULL, if multiple channels and
193915516c77SSepherosa Ziehau 	 * ifnet.if_start method are enabled.
194015516c77SSepherosa Ziehau 	 */
194115516c77SSepherosa Ziehau 	if (txr == NULL || !txr->hn_has_txeof)
194215516c77SSepherosa Ziehau 		return;
194315516c77SSepherosa Ziehau 
194415516c77SSepherosa Ziehau 	txr->hn_txdone_cnt = 0;
194515516c77SSepherosa Ziehau 	hn_txeof(txr);
194615516c77SSepherosa Ziehau }
194715516c77SSepherosa Ziehau 
194815516c77SSepherosa Ziehau static __inline uint32_t
194915516c77SSepherosa Ziehau hn_rndis_pktmsg_offset(uint32_t ofs)
195015516c77SSepherosa Ziehau {
195115516c77SSepherosa Ziehau 
195215516c77SSepherosa Ziehau 	KASSERT(ofs >= sizeof(struct rndis_packet_msg),
195315516c77SSepherosa Ziehau 	    ("invalid RNDIS packet msg offset %u", ofs));
195415516c77SSepherosa Ziehau 	return (ofs - __offsetof(struct rndis_packet_msg, rm_dataoffset));
195515516c77SSepherosa Ziehau }
195615516c77SSepherosa Ziehau 
195715516c77SSepherosa Ziehau static __inline void *
195815516c77SSepherosa Ziehau hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
195915516c77SSepherosa Ziehau     size_t pi_dlen, uint32_t pi_type)
196015516c77SSepherosa Ziehau {
196115516c77SSepherosa Ziehau 	const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
196215516c77SSepherosa Ziehau 	struct rndis_pktinfo *pi;
196315516c77SSepherosa Ziehau 
196415516c77SSepherosa Ziehau 	KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
196515516c77SSepherosa Ziehau 	    ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
196615516c77SSepherosa Ziehau 
196715516c77SSepherosa Ziehau 	/*
196815516c77SSepherosa Ziehau 	 * Per-packet-info does not move; it only grows.
196915516c77SSepherosa Ziehau 	 *
197015516c77SSepherosa Ziehau 	 * NOTE:
197115516c77SSepherosa Ziehau 	 * rm_pktinfooffset in this phase counts from the beginning
197215516c77SSepherosa Ziehau 	 * of rndis_packet_msg.
197315516c77SSepherosa Ziehau 	 */
197415516c77SSepherosa Ziehau 	KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
197515516c77SSepherosa Ziehau 	    ("%u pktinfo overflows RNDIS packet msg", pi_type));
197615516c77SSepherosa Ziehau 	pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
197715516c77SSepherosa Ziehau 	    pkt->rm_pktinfolen);
197815516c77SSepherosa Ziehau 	pkt->rm_pktinfolen += pi_size;
197915516c77SSepherosa Ziehau 
198015516c77SSepherosa Ziehau 	pi->rm_size = pi_size;
198115516c77SSepherosa Ziehau 	pi->rm_type = pi_type;
198215516c77SSepherosa Ziehau 	pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
198315516c77SSepherosa Ziehau 
198415516c77SSepherosa Ziehau 	return (pi->rm_data);
198515516c77SSepherosa Ziehau }
198615516c77SSepherosa Ziehau 
1987dc13fee6SSepherosa Ziehau static __inline int
1988dc13fee6SSepherosa Ziehau hn_flush_txagg(struct ifnet *ifp, struct hn_tx_ring *txr)
1989dc13fee6SSepherosa Ziehau {
1990dc13fee6SSepherosa Ziehau 	struct hn_txdesc *txd;
1991dc13fee6SSepherosa Ziehau 	struct mbuf *m;
1992dc13fee6SSepherosa Ziehau 	int error, pkts;
1993dc13fee6SSepherosa Ziehau 
1994dc13fee6SSepherosa Ziehau 	txd = txr->hn_agg_txd;
1995dc13fee6SSepherosa Ziehau 	KASSERT(txd != NULL, ("no aggregate txdesc"));
1996dc13fee6SSepherosa Ziehau 
1997dc13fee6SSepherosa Ziehau 	/*
1998dc13fee6SSepherosa Ziehau 	 * Since hn_txpkt() will reset this temporary stat, save
1999dc13fee6SSepherosa Ziehau 	 * it now, so that oerrors can be updated properly, if
2000dc13fee6SSepherosa Ziehau 	 * hn_txpkt() ever fails.
2001dc13fee6SSepherosa Ziehau 	 */
2002dc13fee6SSepherosa Ziehau 	pkts = txr->hn_stat_pkts;
2003dc13fee6SSepherosa Ziehau 
2004dc13fee6SSepherosa Ziehau 	/*
2005dc13fee6SSepherosa Ziehau 	 * Since txd's mbuf will _not_ be freed upon hn_txpkt()
2006dc13fee6SSepherosa Ziehau 	 * failure, save it for later freeing, if hn_txpkt() ever
2007dc13fee6SSepherosa Ziehau 	 * fails.
2008dc13fee6SSepherosa Ziehau 	 */
2009dc13fee6SSepherosa Ziehau 	m = txd->m;
2010dc13fee6SSepherosa Ziehau 	error = hn_txpkt(ifp, txr, txd);
2011dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
2012dc13fee6SSepherosa Ziehau 		/* txd is freed, but m is not. */
2013dc13fee6SSepherosa Ziehau 		m_freem(m);
2014dc13fee6SSepherosa Ziehau 
2015dc13fee6SSepherosa Ziehau 		txr->hn_flush_failed++;
2016dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, pkts);
2017dc13fee6SSepherosa Ziehau 	}
2018dc13fee6SSepherosa Ziehau 
2019dc13fee6SSepherosa Ziehau 	/* Reset all aggregation states. */
2020dc13fee6SSepherosa Ziehau 	txr->hn_agg_txd = NULL;
2021dc13fee6SSepherosa Ziehau 	txr->hn_agg_szleft = 0;
2022dc13fee6SSepherosa Ziehau 	txr->hn_agg_pktleft = 0;
2023dc13fee6SSepherosa Ziehau 	txr->hn_agg_prevpkt = NULL;
2024dc13fee6SSepherosa Ziehau 
2025dc13fee6SSepherosa Ziehau 	return (error);
2026dc13fee6SSepherosa Ziehau }
2027dc13fee6SSepherosa Ziehau 
2028dc13fee6SSepherosa Ziehau static void *
2029dc13fee6SSepherosa Ziehau hn_try_txagg(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
2030dc13fee6SSepherosa Ziehau     int pktsize)
2031dc13fee6SSepherosa Ziehau {
2032dc13fee6SSepherosa Ziehau 	void *chim;
2033dc13fee6SSepherosa Ziehau 
2034dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL) {
2035dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft >= 1 && txr->hn_agg_szleft > pktsize) {
2036dc13fee6SSepherosa Ziehau 			struct hn_txdesc *agg_txd = txr->hn_agg_txd;
2037dc13fee6SSepherosa Ziehau 			struct rndis_packet_msg *pkt = txr->hn_agg_prevpkt;
2038dc13fee6SSepherosa Ziehau 			int olen;
2039dc13fee6SSepherosa Ziehau 
2040dc13fee6SSepherosa Ziehau 			/*
2041dc13fee6SSepherosa Ziehau 			 * Update the previous RNDIS packet's total length,
2042dc13fee6SSepherosa Ziehau 			 * it can be increased due to the mandatory alignment
2043dc13fee6SSepherosa Ziehau 			 * padding for this RNDIS packet.  And update the
2044dc13fee6SSepherosa Ziehau 			 * aggregating txdesc's chimney sending buffer size
2045dc13fee6SSepherosa Ziehau 			 * accordingly.
2046dc13fee6SSepherosa Ziehau 			 *
2047dc13fee6SSepherosa Ziehau 			 * XXX
2048dc13fee6SSepherosa Ziehau 			 * Zero-out the padding, as required by the RNDIS spec.
2049dc13fee6SSepherosa Ziehau 			 */
2050dc13fee6SSepherosa Ziehau 			olen = pkt->rm_len;
2051dc13fee6SSepherosa Ziehau 			pkt->rm_len = roundup2(olen, txr->hn_agg_align);
2052dc13fee6SSepherosa Ziehau 			agg_txd->chim_size += pkt->rm_len - olen;
2053dc13fee6SSepherosa Ziehau 
2054dc13fee6SSepherosa Ziehau 			/* Link this txdesc to the parent. */
2055dc13fee6SSepherosa Ziehau 			hn_txdesc_agg(agg_txd, txd);
2056dc13fee6SSepherosa Ziehau 
2057dc13fee6SSepherosa Ziehau 			chim = (uint8_t *)pkt + pkt->rm_len;
2058dc13fee6SSepherosa Ziehau 			/* Save the current packet for later fixup. */
2059dc13fee6SSepherosa Ziehau 			txr->hn_agg_prevpkt = chim;
2060dc13fee6SSepherosa Ziehau 
2061dc13fee6SSepherosa Ziehau 			txr->hn_agg_pktleft--;
2062dc13fee6SSepherosa Ziehau 			txr->hn_agg_szleft -= pktsize;
2063dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_szleft <=
2064dc13fee6SSepherosa Ziehau 			    HN_PKTSIZE_MIN(txr->hn_agg_align)) {
2065dc13fee6SSepherosa Ziehau 				/*
2066dc13fee6SSepherosa Ziehau 				 * Probably can't aggregate more packets,
2067dc13fee6SSepherosa Ziehau 				 * flush this aggregating txdesc proactively.
2068dc13fee6SSepherosa Ziehau 				 */
2069dc13fee6SSepherosa Ziehau 				txr->hn_agg_pktleft = 0;
2070dc13fee6SSepherosa Ziehau 			}
2071dc13fee6SSepherosa Ziehau 			/* Done! */
2072dc13fee6SSepherosa Ziehau 			return (chim);
2073dc13fee6SSepherosa Ziehau 		}
2074dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
2075dc13fee6SSepherosa Ziehau 	}
2076dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
2077dc13fee6SSepherosa Ziehau 
2078dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney_tried++;
2079dc13fee6SSepherosa Ziehau 	txd->chim_index = hn_chim_alloc(txr->hn_sc);
2080dc13fee6SSepherosa Ziehau 	if (txd->chim_index == HN_NVS_CHIM_IDX_INVALID)
2081dc13fee6SSepherosa Ziehau 		return (NULL);
2082dc13fee6SSepherosa Ziehau 	txr->hn_tx_chimney++;
2083dc13fee6SSepherosa Ziehau 
2084dc13fee6SSepherosa Ziehau 	chim = txr->hn_sc->hn_chim +
2085dc13fee6SSepherosa Ziehau 	    (txd->chim_index * txr->hn_sc->hn_chim_szmax);
2086dc13fee6SSepherosa Ziehau 
2087dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_pktmax > 1 &&
2088dc13fee6SSepherosa Ziehau 	    txr->hn_agg_szmax > pktsize + HN_PKTSIZE_MIN(txr->hn_agg_align)) {
2089dc13fee6SSepherosa Ziehau 		txr->hn_agg_txd = txd;
2090dc13fee6SSepherosa Ziehau 		txr->hn_agg_pktleft = txr->hn_agg_pktmax - 1;
2091dc13fee6SSepherosa Ziehau 		txr->hn_agg_szleft = txr->hn_agg_szmax - pktsize;
2092dc13fee6SSepherosa Ziehau 		txr->hn_agg_prevpkt = chim;
2093dc13fee6SSepherosa Ziehau 	}
2094dc13fee6SSepherosa Ziehau 	return (chim);
2095dc13fee6SSepherosa Ziehau }
2096dc13fee6SSepherosa Ziehau 
209715516c77SSepherosa Ziehau /*
209815516c77SSepherosa Ziehau  * NOTE:
209915516c77SSepherosa Ziehau  * If this function fails, then both txd and m_head0 will be freed.
210015516c77SSepherosa Ziehau  */
210115516c77SSepherosa Ziehau static int
2102dc13fee6SSepherosa Ziehau hn_encap(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd,
2103dc13fee6SSepherosa Ziehau     struct mbuf **m_head0)
210415516c77SSepherosa Ziehau {
210515516c77SSepherosa Ziehau 	bus_dma_segment_t segs[HN_TX_DATA_SEGCNT_MAX];
210615516c77SSepherosa Ziehau 	int error, nsegs, i;
210715516c77SSepherosa Ziehau 	struct mbuf *m_head = *m_head0;
210815516c77SSepherosa Ziehau 	struct rndis_packet_msg *pkt;
210915516c77SSepherosa Ziehau 	uint32_t *pi_data;
21108966e5d5SSepherosa Ziehau 	void *chim = NULL;
2111dc13fee6SSepherosa Ziehau 	int pkt_hlen, pkt_size;
211215516c77SSepherosa Ziehau 
211315516c77SSepherosa Ziehau 	pkt = txd->rndis_pkt;
2114dc13fee6SSepherosa Ziehau 	pkt_size = HN_PKTSIZE(m_head, txr->hn_agg_align);
2115dc13fee6SSepherosa Ziehau 	if (pkt_size < txr->hn_chim_size) {
2116dc13fee6SSepherosa Ziehau 		chim = hn_try_txagg(ifp, txr, txd, pkt_size);
2117dc13fee6SSepherosa Ziehau 		if (chim != NULL)
21188966e5d5SSepherosa Ziehau 			pkt = chim;
2119dc13fee6SSepherosa Ziehau 	} else {
2120dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL)
2121dc13fee6SSepherosa Ziehau 			hn_flush_txagg(ifp, txr);
21228966e5d5SSepherosa Ziehau 	}
21238966e5d5SSepherosa Ziehau 
212415516c77SSepherosa Ziehau 	pkt->rm_type = REMOTE_NDIS_PACKET_MSG;
21258fe90f73SSepherosa Ziehau 	pkt->rm_len = m_head->m_pkthdr.len;
21269130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = 0;
212715516c77SSepherosa Ziehau 	pkt->rm_datalen = m_head->m_pkthdr.len;
2128dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataoffset = 0;
2129dc13fee6SSepherosa Ziehau 	pkt->rm_oobdatalen = 0;
2130dc13fee6SSepherosa Ziehau 	pkt->rm_oobdataelements = 0;
213115516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = sizeof(*pkt);
213215516c77SSepherosa Ziehau 	pkt->rm_pktinfolen = 0;
2133dc13fee6SSepherosa Ziehau 	pkt->rm_vchandle = 0;
2134dc13fee6SSepherosa Ziehau 	pkt->rm_reserved = 0;
213515516c77SSepherosa Ziehau 
213615516c77SSepherosa Ziehau 	if (txr->hn_tx_flags & HN_TX_FLAG_HASHVAL) {
213715516c77SSepherosa Ziehau 		/*
213815516c77SSepherosa Ziehau 		 * Set the hash value for this packet, so that the host could
213915516c77SSepherosa Ziehau 		 * dispatch the TX done event for this packet back to this TX
214015516c77SSepherosa Ziehau 		 * ring's channel.
214115516c77SSepherosa Ziehau 		 */
214215516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
214315516c77SSepherosa Ziehau 		    HN_NDIS_HASH_VALUE_SIZE, HN_NDIS_PKTINFO_TYPE_HASHVAL);
214415516c77SSepherosa Ziehau 		*pi_data = txr->hn_tx_idx;
214515516c77SSepherosa Ziehau 	}
214615516c77SSepherosa Ziehau 
214715516c77SSepherosa Ziehau 	if (m_head->m_flags & M_VLANTAG) {
214815516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
214915516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_SIZE, NDIS_PKTINFO_TYPE_VLAN);
215015516c77SSepherosa Ziehau 		*pi_data = NDIS_VLAN_INFO_MAKE(
215115516c77SSepherosa Ziehau 		    EVL_VLANOFTAG(m_head->m_pkthdr.ether_vtag),
215215516c77SSepherosa Ziehau 		    EVL_PRIOFTAG(m_head->m_pkthdr.ether_vtag),
215315516c77SSepherosa Ziehau 		    EVL_CFIOFTAG(m_head->m_pkthdr.ether_vtag));
215415516c77SSepherosa Ziehau 	}
215515516c77SSepherosa Ziehau 
215615516c77SSepherosa Ziehau 	if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
215715516c77SSepherosa Ziehau #if defined(INET6) || defined(INET)
215815516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
215915516c77SSepherosa Ziehau 		    NDIS_LSO2_INFO_SIZE, NDIS_PKTINFO_TYPE_LSO);
216015516c77SSepherosa Ziehau #ifdef INET
216115516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
216215516c77SSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV4(0,
216315516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
216415516c77SSepherosa Ziehau 		}
216515516c77SSepherosa Ziehau #endif
216615516c77SSepherosa Ziehau #if defined(INET6) && defined(INET)
216715516c77SSepherosa Ziehau 		else
216815516c77SSepherosa Ziehau #endif
216915516c77SSepherosa Ziehau #ifdef INET6
217015516c77SSepherosa Ziehau 		{
217115516c77SSepherosa Ziehau 			*pi_data = NDIS_LSO2_INFO_MAKEIPV6(0,
217215516c77SSepherosa Ziehau 			    m_head->m_pkthdr.tso_segsz);
217315516c77SSepherosa Ziehau 		}
217415516c77SSepherosa Ziehau #endif
217515516c77SSepherosa Ziehau #endif	/* INET6 || INET */
217615516c77SSepherosa Ziehau 	} else if (m_head->m_pkthdr.csum_flags & txr->hn_csum_assist) {
217715516c77SSepherosa Ziehau 		pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
217815516c77SSepherosa Ziehau 		    NDIS_TXCSUM_INFO_SIZE, NDIS_PKTINFO_TYPE_CSUM);
217915516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags &
218015516c77SSepherosa Ziehau 		    (CSUM_IP6_TCP | CSUM_IP6_UDP)) {
218115516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV6;
218215516c77SSepherosa Ziehau 		} else {
218315516c77SSepherosa Ziehau 			*pi_data = NDIS_TXCSUM_INFO_IPV4;
218415516c77SSepherosa Ziehau 			if (m_head->m_pkthdr.csum_flags & CSUM_IP)
218515516c77SSepherosa Ziehau 				*pi_data |= NDIS_TXCSUM_INFO_IPCS;
218615516c77SSepherosa Ziehau 		}
218715516c77SSepherosa Ziehau 
218815516c77SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP6_TCP))
218915516c77SSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_TCPCS;
219015516c77SSepherosa Ziehau 		else if (m_head->m_pkthdr.csum_flags &
219115516c77SSepherosa Ziehau 		    (CSUM_IP_UDP | CSUM_IP6_UDP))
219215516c77SSepherosa Ziehau 			*pi_data |= NDIS_TXCSUM_INFO_UDPCS;
219315516c77SSepherosa Ziehau 	}
219415516c77SSepherosa Ziehau 
2195dc13fee6SSepherosa Ziehau 	pkt_hlen = pkt->rm_pktinfooffset + pkt->rm_pktinfolen;
21968fe90f73SSepherosa Ziehau 	/* Fixup RNDIS packet message total length */
21978fe90f73SSepherosa Ziehau 	pkt->rm_len += pkt_hlen;
219815516c77SSepherosa Ziehau 	/* Convert RNDIS packet message offsets */
21999130c4f7SSepherosa Ziehau 	pkt->rm_dataoffset = hn_rndis_pktmsg_offset(pkt_hlen);
220015516c77SSepherosa Ziehau 	pkt->rm_pktinfooffset = hn_rndis_pktmsg_offset(pkt->rm_pktinfooffset);
220115516c77SSepherosa Ziehau 
220215516c77SSepherosa Ziehau 	/*
22038966e5d5SSepherosa Ziehau 	 * Fast path: Chimney sending.
220415516c77SSepherosa Ziehau 	 */
22058966e5d5SSepherosa Ziehau 	if (chim != NULL) {
2206dc13fee6SSepherosa Ziehau 		struct hn_txdesc *tgt_txd = txd;
2207dc13fee6SSepherosa Ziehau 
2208dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_txd != NULL) {
2209dc13fee6SSepherosa Ziehau 			tgt_txd = txr->hn_agg_txd;
2210dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
2211dc13fee6SSepherosa Ziehau 			*m_head0 = NULL;
2212dc13fee6SSepherosa Ziehau #endif
2213dc13fee6SSepherosa Ziehau 		}
2214dc13fee6SSepherosa Ziehau 
2215dc13fee6SSepherosa Ziehau 		KASSERT(pkt == chim,
2216dc13fee6SSepherosa Ziehau 		    ("RNDIS pkt not in chimney sending buffer"));
2217dc13fee6SSepherosa Ziehau 		KASSERT(tgt_txd->chim_index != HN_NVS_CHIM_IDX_INVALID,
2218dc13fee6SSepherosa Ziehau 		    ("chimney sending buffer is not used"));
2219dc13fee6SSepherosa Ziehau 		tgt_txd->chim_size += pkt->rm_len;
222015516c77SSepherosa Ziehau 
22218966e5d5SSepherosa Ziehau 		m_copydata(m_head, 0, m_head->m_pkthdr.len,
2222dc13fee6SSepherosa Ziehau 		    ((uint8_t *)chim) + pkt_hlen);
222315516c77SSepherosa Ziehau 
222415516c77SSepherosa Ziehau 		txr->hn_gpa_cnt = 0;
222515516c77SSepherosa Ziehau 		txr->hn_sendpkt = hn_txpkt_chim;
222615516c77SSepherosa Ziehau 		goto done;
222715516c77SSepherosa Ziehau 	}
2228dc13fee6SSepherosa Ziehau 
2229dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("aggregating sglist txdesc"));
22308966e5d5SSepherosa Ziehau 	KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
22318966e5d5SSepherosa Ziehau 	    ("chimney buffer is used"));
22328966e5d5SSepherosa Ziehau 	KASSERT(pkt == txd->rndis_pkt, ("RNDIS pkt not in txdesc"));
223315516c77SSepherosa Ziehau 
223415516c77SSepherosa Ziehau 	error = hn_txdesc_dmamap_load(txr, txd, &m_head, segs, &nsegs);
2235dc13fee6SSepherosa Ziehau 	if (__predict_false(error)) {
223615516c77SSepherosa Ziehau 		int freed;
223715516c77SSepherosa Ziehau 
223815516c77SSepherosa Ziehau 		/*
223915516c77SSepherosa Ziehau 		 * This mbuf is not linked w/ the txd yet, so free it now.
224015516c77SSepherosa Ziehau 		 */
224115516c77SSepherosa Ziehau 		m_freem(m_head);
224215516c77SSepherosa Ziehau 		*m_head0 = NULL;
224315516c77SSepherosa Ziehau 
224415516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
224515516c77SSepherosa Ziehau 		KASSERT(freed != 0,
224615516c77SSepherosa Ziehau 		    ("fail to free txd upon txdma error"));
224715516c77SSepherosa Ziehau 
224815516c77SSepherosa Ziehau 		txr->hn_txdma_failed++;
2249dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
225015516c77SSepherosa Ziehau 		return error;
225115516c77SSepherosa Ziehau 	}
225215516c77SSepherosa Ziehau 	*m_head0 = m_head;
225315516c77SSepherosa Ziehau 
225415516c77SSepherosa Ziehau 	/* +1 RNDIS packet message */
225515516c77SSepherosa Ziehau 	txr->hn_gpa_cnt = nsegs + 1;
225615516c77SSepherosa Ziehau 
225715516c77SSepherosa Ziehau 	/* send packet with page buffer */
225815516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_page = atop(txd->rndis_pkt_paddr);
225915516c77SSepherosa Ziehau 	txr->hn_gpa[0].gpa_ofs = txd->rndis_pkt_paddr & PAGE_MASK;
2260dc13fee6SSepherosa Ziehau 	txr->hn_gpa[0].gpa_len = pkt_hlen;
226115516c77SSepherosa Ziehau 
226215516c77SSepherosa Ziehau 	/*
226315516c77SSepherosa Ziehau 	 * Fill the page buffers with mbuf info after the page
226415516c77SSepherosa Ziehau 	 * buffer for RNDIS packet message.
226515516c77SSepherosa Ziehau 	 */
226615516c77SSepherosa Ziehau 	for (i = 0; i < nsegs; ++i) {
226715516c77SSepherosa Ziehau 		struct vmbus_gpa *gpa = &txr->hn_gpa[i + 1];
226815516c77SSepherosa Ziehau 
226915516c77SSepherosa Ziehau 		gpa->gpa_page = atop(segs[i].ds_addr);
227015516c77SSepherosa Ziehau 		gpa->gpa_ofs = segs[i].ds_addr & PAGE_MASK;
227115516c77SSepherosa Ziehau 		gpa->gpa_len = segs[i].ds_len;
227215516c77SSepherosa Ziehau 	}
227315516c77SSepherosa Ziehau 
227415516c77SSepherosa Ziehau 	txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
227515516c77SSepherosa Ziehau 	txd->chim_size = 0;
227615516c77SSepherosa Ziehau 	txr->hn_sendpkt = hn_txpkt_sglist;
227715516c77SSepherosa Ziehau done:
227815516c77SSepherosa Ziehau 	txd->m = m_head;
227915516c77SSepherosa Ziehau 
228015516c77SSepherosa Ziehau 	/* Set the completion routine */
228115516c77SSepherosa Ziehau 	hn_nvs_sendctx_init(&txd->send_ctx, hn_txpkt_done, txd);
228215516c77SSepherosa Ziehau 
2283dc13fee6SSepherosa Ziehau 	/* Update temporary stats for later use. */
2284dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts++;
2285dc13fee6SSepherosa Ziehau 	txr->hn_stat_size += m_head->m_pkthdr.len;
2286dc13fee6SSepherosa Ziehau 	if (m_head->m_flags & M_MCAST)
2287dc13fee6SSepherosa Ziehau 		txr->hn_stat_mcasts++;
2288dc13fee6SSepherosa Ziehau 
228915516c77SSepherosa Ziehau 	return 0;
229015516c77SSepherosa Ziehau }
229115516c77SSepherosa Ziehau 
229215516c77SSepherosa Ziehau /*
229315516c77SSepherosa Ziehau  * NOTE:
229415516c77SSepherosa Ziehau  * If this function fails, then txd will be freed, but the mbuf
229515516c77SSepherosa Ziehau  * associated w/ the txd will _not_ be freed.
229615516c77SSepherosa Ziehau  */
229715516c77SSepherosa Ziehau static int
229815516c77SSepherosa Ziehau hn_txpkt(struct ifnet *ifp, struct hn_tx_ring *txr, struct hn_txdesc *txd)
229915516c77SSepherosa Ziehau {
23008e7d3136SSepherosa Ziehau 	int error, send_failed = 0, has_bpf;
230115516c77SSepherosa Ziehau 
230215516c77SSepherosa Ziehau again:
23038e7d3136SSepherosa Ziehau 	has_bpf = bpf_peers_present(ifp->if_bpf);
23048e7d3136SSepherosa Ziehau 	if (has_bpf) {
230515516c77SSepherosa Ziehau 		/*
23068e7d3136SSepherosa Ziehau 		 * Make sure that this txd and any aggregated txds are not
23078e7d3136SSepherosa Ziehau 		 * freed before ETHER_BPF_MTAP.
230815516c77SSepherosa Ziehau 		 */
230915516c77SSepherosa Ziehau 		hn_txdesc_hold(txd);
23108e7d3136SSepherosa Ziehau 	}
231115516c77SSepherosa Ziehau 	error = txr->hn_sendpkt(txr, txd);
231215516c77SSepherosa Ziehau 	if (!error) {
23138e7d3136SSepherosa Ziehau 		if (has_bpf) {
2314dc13fee6SSepherosa Ziehau 			const struct hn_txdesc *tmp_txd;
2315dc13fee6SSepherosa Ziehau 
231615516c77SSepherosa Ziehau 			ETHER_BPF_MTAP(ifp, txd->m);
2317dc13fee6SSepherosa Ziehau 			STAILQ_FOREACH(tmp_txd, &txd->agg_list, agg_link)
2318dc13fee6SSepherosa Ziehau 				ETHER_BPF_MTAP(ifp, tmp_txd->m);
2319dc13fee6SSepherosa Ziehau 		}
2320dc13fee6SSepherosa Ziehau 
2321dc13fee6SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, txr->hn_stat_pkts);
232223bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
232323bf9e15SSepherosa Ziehau 		if (!hn_use_if_start)
232423bf9e15SSepherosa Ziehau #endif
232523bf9e15SSepherosa Ziehau 		{
232615516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OBYTES,
2327dc13fee6SSepherosa Ziehau 			    txr->hn_stat_size);
2328dc13fee6SSepherosa Ziehau 			if (txr->hn_stat_mcasts != 0) {
2329dc13fee6SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OMCASTS,
2330dc13fee6SSepherosa Ziehau 				    txr->hn_stat_mcasts);
233115516c77SSepherosa Ziehau 			}
2332dc13fee6SSepherosa Ziehau 		}
2333dc13fee6SSepherosa Ziehau 		txr->hn_pkts += txr->hn_stat_pkts;
2334dc13fee6SSepherosa Ziehau 		txr->hn_sends++;
233515516c77SSepherosa Ziehau 	}
23368e7d3136SSepherosa Ziehau 	if (has_bpf)
233715516c77SSepherosa Ziehau 		hn_txdesc_put(txr, txd);
233815516c77SSepherosa Ziehau 
233915516c77SSepherosa Ziehau 	if (__predict_false(error)) {
234015516c77SSepherosa Ziehau 		int freed;
234115516c77SSepherosa Ziehau 
234215516c77SSepherosa Ziehau 		/*
234315516c77SSepherosa Ziehau 		 * This should "really rarely" happen.
234415516c77SSepherosa Ziehau 		 *
234515516c77SSepherosa Ziehau 		 * XXX Too many RX to be acked or too many sideband
234615516c77SSepherosa Ziehau 		 * commands to run?  Ask netvsc_channel_rollup()
234715516c77SSepherosa Ziehau 		 * to kick start later.
234815516c77SSepherosa Ziehau 		 */
234915516c77SSepherosa Ziehau 		txr->hn_has_txeof = 1;
235015516c77SSepherosa Ziehau 		if (!send_failed) {
235115516c77SSepherosa Ziehau 			txr->hn_send_failed++;
235215516c77SSepherosa Ziehau 			send_failed = 1;
235315516c77SSepherosa Ziehau 			/*
235415516c77SSepherosa Ziehau 			 * Try sending again after set hn_has_txeof;
235515516c77SSepherosa Ziehau 			 * in case that we missed the last
235615516c77SSepherosa Ziehau 			 * netvsc_channel_rollup().
235715516c77SSepherosa Ziehau 			 */
235815516c77SSepherosa Ziehau 			goto again;
235915516c77SSepherosa Ziehau 		}
236015516c77SSepherosa Ziehau 		if_printf(ifp, "send failed\n");
236115516c77SSepherosa Ziehau 
236215516c77SSepherosa Ziehau 		/*
236315516c77SSepherosa Ziehau 		 * Caller will perform further processing on the
236415516c77SSepherosa Ziehau 		 * associated mbuf, so don't free it in hn_txdesc_put();
236515516c77SSepherosa Ziehau 		 * only unload it from the DMA map in hn_txdesc_put(),
236615516c77SSepherosa Ziehau 		 * if it was loaded.
236715516c77SSepherosa Ziehau 		 */
236815516c77SSepherosa Ziehau 		txd->m = NULL;
236915516c77SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
237015516c77SSepherosa Ziehau 		KASSERT(freed != 0,
237115516c77SSepherosa Ziehau 		    ("fail to free txd upon send error"));
237215516c77SSepherosa Ziehau 
237315516c77SSepherosa Ziehau 		txr->hn_send_failed++;
237415516c77SSepherosa Ziehau 	}
2375dc13fee6SSepherosa Ziehau 
2376dc13fee6SSepherosa Ziehau 	/* Reset temporary stats, after this sending is done. */
2377dc13fee6SSepherosa Ziehau 	txr->hn_stat_size = 0;
2378dc13fee6SSepherosa Ziehau 	txr->hn_stat_pkts = 0;
2379dc13fee6SSepherosa Ziehau 	txr->hn_stat_mcasts = 0;
2380dc13fee6SSepherosa Ziehau 
2381dc13fee6SSepherosa Ziehau 	return (error);
238215516c77SSepherosa Ziehau }
238315516c77SSepherosa Ziehau 
238415516c77SSepherosa Ziehau /*
238515516c77SSepherosa Ziehau  * Append the specified data to the indicated mbuf chain,
238615516c77SSepherosa Ziehau  * Extend the mbuf chain if the new data does not fit in
238715516c77SSepherosa Ziehau  * existing space.
238815516c77SSepherosa Ziehau  *
238915516c77SSepherosa Ziehau  * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
239015516c77SSepherosa Ziehau  * There should be an equivalent in the kernel mbuf code,
239115516c77SSepherosa Ziehau  * but there does not appear to be one yet.
239215516c77SSepherosa Ziehau  *
239315516c77SSepherosa Ziehau  * Differs from m_append() in that additional mbufs are
239415516c77SSepherosa Ziehau  * allocated with cluster size MJUMPAGESIZE, and filled
239515516c77SSepherosa Ziehau  * accordingly.
239615516c77SSepherosa Ziehau  *
239715516c77SSepherosa Ziehau  * Return 1 if able to complete the job; otherwise 0.
239815516c77SSepherosa Ziehau  */
239915516c77SSepherosa Ziehau static int
240015516c77SSepherosa Ziehau hv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
240115516c77SSepherosa Ziehau {
240215516c77SSepherosa Ziehau 	struct mbuf *m, *n;
240315516c77SSepherosa Ziehau 	int remainder, space;
240415516c77SSepherosa Ziehau 
240515516c77SSepherosa Ziehau 	for (m = m0; m->m_next != NULL; m = m->m_next)
240615516c77SSepherosa Ziehau 		;
240715516c77SSepherosa Ziehau 	remainder = len;
240815516c77SSepherosa Ziehau 	space = M_TRAILINGSPACE(m);
240915516c77SSepherosa Ziehau 	if (space > 0) {
241015516c77SSepherosa Ziehau 		/*
241115516c77SSepherosa Ziehau 		 * Copy into available space.
241215516c77SSepherosa Ziehau 		 */
241315516c77SSepherosa Ziehau 		if (space > remainder)
241415516c77SSepherosa Ziehau 			space = remainder;
241515516c77SSepherosa Ziehau 		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
241615516c77SSepherosa Ziehau 		m->m_len += space;
241715516c77SSepherosa Ziehau 		cp += space;
241815516c77SSepherosa Ziehau 		remainder -= space;
241915516c77SSepherosa Ziehau 	}
242015516c77SSepherosa Ziehau 	while (remainder > 0) {
242115516c77SSepherosa Ziehau 		/*
242215516c77SSepherosa Ziehau 		 * Allocate a new mbuf; could check space
242315516c77SSepherosa Ziehau 		 * and allocate a cluster instead.
242415516c77SSepherosa Ziehau 		 */
242515516c77SSepherosa Ziehau 		n = m_getjcl(M_NOWAIT, m->m_type, 0, MJUMPAGESIZE);
242615516c77SSepherosa Ziehau 		if (n == NULL)
242715516c77SSepherosa Ziehau 			break;
242815516c77SSepherosa Ziehau 		n->m_len = min(MJUMPAGESIZE, remainder);
242915516c77SSepherosa Ziehau 		bcopy(cp, mtod(n, caddr_t), n->m_len);
243015516c77SSepherosa Ziehau 		cp += n->m_len;
243115516c77SSepherosa Ziehau 		remainder -= n->m_len;
243215516c77SSepherosa Ziehau 		m->m_next = n;
243315516c77SSepherosa Ziehau 		m = n;
243415516c77SSepherosa Ziehau 	}
243515516c77SSepherosa Ziehau 	if (m0->m_flags & M_PKTHDR)
243615516c77SSepherosa Ziehau 		m0->m_pkthdr.len += len - remainder;
243715516c77SSepherosa Ziehau 
243815516c77SSepherosa Ziehau 	return (remainder == 0);
243915516c77SSepherosa Ziehau }
244015516c77SSepherosa Ziehau 
244115516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
244215516c77SSepherosa Ziehau static __inline int
244315516c77SSepherosa Ziehau hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m)
244415516c77SSepherosa Ziehau {
244515516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
244615516c77SSepherosa Ziehau 	if (hn_lro_mbufq_depth) {
244715516c77SSepherosa Ziehau 		tcp_lro_queue_mbuf(lc, m);
244815516c77SSepherosa Ziehau 		return 0;
244915516c77SSepherosa Ziehau 	}
245015516c77SSepherosa Ziehau #endif
245115516c77SSepherosa Ziehau 	return tcp_lro_rx(lc, m, 0);
245215516c77SSepherosa Ziehau }
245315516c77SSepherosa Ziehau #endif
245415516c77SSepherosa Ziehau 
245515516c77SSepherosa Ziehau static int
245615516c77SSepherosa Ziehau hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
245715516c77SSepherosa Ziehau     const struct hn_rxinfo *info)
245815516c77SSepherosa Ziehau {
24595bdfd3fdSDexuan Cui 	struct ifnet *ifp;
246015516c77SSepherosa Ziehau 	struct mbuf *m_new;
246115516c77SSepherosa Ziehau 	int size, do_lro = 0, do_csum = 1;
246215516c77SSepherosa Ziehau 	int hash_type;
246315516c77SSepherosa Ziehau 
24645bdfd3fdSDexuan Cui 	/* If the VF is active, inject the packet through the VF */
2465499c3e17SSepherosa Ziehau 	ifp = rxr->hn_rxvf_ifp ? rxr->hn_rxvf_ifp : rxr->hn_ifp;
24665bdfd3fdSDexuan Cui 
2467b3b75d9cSSepherosa Ziehau 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
2468b3b75d9cSSepherosa Ziehau 		/*
2469b3b75d9cSSepherosa Ziehau 		 * NOTE:
2470b3b75d9cSSepherosa Ziehau 		 * See the NOTE of hn_rndis_init_fixat().  This
2471b3b75d9cSSepherosa Ziehau 		 * function can be reached, immediately after the
2472b3b75d9cSSepherosa Ziehau 		 * RNDIS is initialized but before the ifnet is
2473b3b75d9cSSepherosa Ziehau 		 * setup on the hn_attach() path; drop the unexpected
2474b3b75d9cSSepherosa Ziehau 		 * packets.
2475b3b75d9cSSepherosa Ziehau 		 */
2476b3b75d9cSSepherosa Ziehau 		return (0);
2477b3b75d9cSSepherosa Ziehau 	}
2478b3b75d9cSSepherosa Ziehau 
2479c927d681SDexuan Cui 	if (dlen <= MHLEN) {
248015516c77SSepherosa Ziehau 		m_new = m_gethdr(M_NOWAIT, MT_DATA);
248115516c77SSepherosa Ziehau 		if (m_new == NULL) {
248215516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
248315516c77SSepherosa Ziehau 			return (0);
248415516c77SSepherosa Ziehau 		}
248515516c77SSepherosa Ziehau 		memcpy(mtod(m_new, void *), data, dlen);
248615516c77SSepherosa Ziehau 		m_new->m_pkthdr.len = m_new->m_len = dlen;
248715516c77SSepherosa Ziehau 		rxr->hn_small_pkts++;
248815516c77SSepherosa Ziehau 	} else {
248915516c77SSepherosa Ziehau 		/*
249015516c77SSepherosa Ziehau 		 * Get an mbuf with a cluster.  For packets 2K or less,
249115516c77SSepherosa Ziehau 		 * get a standard 2K cluster.  For anything larger, get a
249215516c77SSepherosa Ziehau 		 * 4K cluster.  Any buffers larger than 4K can cause problems
249315516c77SSepherosa Ziehau 		 * if looped around to the Hyper-V TX channel, so avoid them.
249415516c77SSepherosa Ziehau 		 */
249515516c77SSepherosa Ziehau 		size = MCLBYTES;
249615516c77SSepherosa Ziehau 		if (dlen > MCLBYTES) {
249715516c77SSepherosa Ziehau 			/* 4096 */
249815516c77SSepherosa Ziehau 			size = MJUMPAGESIZE;
249915516c77SSepherosa Ziehau 		}
250015516c77SSepherosa Ziehau 
250115516c77SSepherosa Ziehau 		m_new = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, size);
250215516c77SSepherosa Ziehau 		if (m_new == NULL) {
250315516c77SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
250415516c77SSepherosa Ziehau 			return (0);
250515516c77SSepherosa Ziehau 		}
250615516c77SSepherosa Ziehau 
250715516c77SSepherosa Ziehau 		hv_m_append(m_new, dlen, data);
250815516c77SSepherosa Ziehau 	}
250915516c77SSepherosa Ziehau 	m_new->m_pkthdr.rcvif = ifp;
251015516c77SSepherosa Ziehau 
251115516c77SSepherosa Ziehau 	if (__predict_false((ifp->if_capenable & IFCAP_RXCSUM) == 0))
251215516c77SSepherosa Ziehau 		do_csum = 0;
251315516c77SSepherosa Ziehau 
251415516c77SSepherosa Ziehau 	/* receive side checksum offload */
251515516c77SSepherosa Ziehau 	if (info->csum_info != HN_NDIS_RXCSUM_INFO_INVALID) {
251615516c77SSepherosa Ziehau 		/* IP csum offload */
251715516c77SSepherosa Ziehau 		if ((info->csum_info & NDIS_RXCSUM_INFO_IPCS_OK) && do_csum) {
251815516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
251915516c77SSepherosa Ziehau 			    (CSUM_IP_CHECKED | CSUM_IP_VALID);
252015516c77SSepherosa Ziehau 			rxr->hn_csum_ip++;
252115516c77SSepherosa Ziehau 		}
252215516c77SSepherosa Ziehau 
252315516c77SSepherosa Ziehau 		/* TCP/UDP csum offload */
252415516c77SSepherosa Ziehau 		if ((info->csum_info & (NDIS_RXCSUM_INFO_UDPCS_OK |
252515516c77SSepherosa Ziehau 		     NDIS_RXCSUM_INFO_TCPCS_OK)) && do_csum) {
252615516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_flags |=
252715516c77SSepherosa Ziehau 			    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
252815516c77SSepherosa Ziehau 			m_new->m_pkthdr.csum_data = 0xffff;
252915516c77SSepherosa Ziehau 			if (info->csum_info & NDIS_RXCSUM_INFO_TCPCS_OK)
253015516c77SSepherosa Ziehau 				rxr->hn_csum_tcp++;
253115516c77SSepherosa Ziehau 			else
253215516c77SSepherosa Ziehau 				rxr->hn_csum_udp++;
253315516c77SSepherosa Ziehau 		}
253415516c77SSepherosa Ziehau 
253515516c77SSepherosa Ziehau 		/*
253615516c77SSepherosa Ziehau 		 * XXX
253715516c77SSepherosa Ziehau 		 * As of this write (Oct 28th, 2016), host side will turn
253815516c77SSepherosa Ziehau 		 * on only TCPCS_OK and IPCS_OK even for UDP datagrams, so
253915516c77SSepherosa Ziehau 		 * the do_lro setting here is actually _not_ accurate.  We
254015516c77SSepherosa Ziehau 		 * depend on the RSS hash type check to reset do_lro.
254115516c77SSepherosa Ziehau 		 */
254215516c77SSepherosa Ziehau 		if ((info->csum_info &
254315516c77SSepherosa Ziehau 		     (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) ==
254415516c77SSepherosa Ziehau 		    (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK))
254515516c77SSepherosa Ziehau 			do_lro = 1;
254615516c77SSepherosa Ziehau 	} else {
254715516c77SSepherosa Ziehau 		const struct ether_header *eh;
254815516c77SSepherosa Ziehau 		uint16_t etype;
254915516c77SSepherosa Ziehau 		int hoff;
255015516c77SSepherosa Ziehau 
255115516c77SSepherosa Ziehau 		hoff = sizeof(*eh);
255215516c77SSepherosa Ziehau 		if (m_new->m_len < hoff)
255315516c77SSepherosa Ziehau 			goto skip;
255415516c77SSepherosa Ziehau 		eh = mtod(m_new, struct ether_header *);
255515516c77SSepherosa Ziehau 		etype = ntohs(eh->ether_type);
255615516c77SSepherosa Ziehau 		if (etype == ETHERTYPE_VLAN) {
255715516c77SSepherosa Ziehau 			const struct ether_vlan_header *evl;
255815516c77SSepherosa Ziehau 
255915516c77SSepherosa Ziehau 			hoff = sizeof(*evl);
256015516c77SSepherosa Ziehau 			if (m_new->m_len < hoff)
256115516c77SSepherosa Ziehau 				goto skip;
256215516c77SSepherosa Ziehau 			evl = mtod(m_new, struct ether_vlan_header *);
256315516c77SSepherosa Ziehau 			etype = ntohs(evl->evl_proto);
256415516c77SSepherosa Ziehau 		}
256515516c77SSepherosa Ziehau 
256615516c77SSepherosa Ziehau 		if (etype == ETHERTYPE_IP) {
256715516c77SSepherosa Ziehau 			int pr;
256815516c77SSepherosa Ziehau 
256915516c77SSepherosa Ziehau 			pr = hn_check_iplen(m_new, hoff);
257015516c77SSepherosa Ziehau 			if (pr == IPPROTO_TCP) {
257115516c77SSepherosa Ziehau 				if (do_csum &&
257215516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
257315516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_TCP)) {
257415516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
257515516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
257615516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
257715516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
257815516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
257915516c77SSepherosa Ziehau 				}
258015516c77SSepherosa Ziehau 				do_lro = 1;
258115516c77SSepherosa Ziehau 			} else if (pr == IPPROTO_UDP) {
258215516c77SSepherosa Ziehau 				if (do_csum &&
258315516c77SSepherosa Ziehau 				    (rxr->hn_trust_hcsum &
258415516c77SSepherosa Ziehau 				     HN_TRUST_HCSUM_UDP)) {
258515516c77SSepherosa Ziehau 					rxr->hn_csum_trusted++;
258615516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_flags |=
258715516c77SSepherosa Ziehau 					   (CSUM_IP_CHECKED | CSUM_IP_VALID |
258815516c77SSepherosa Ziehau 					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
258915516c77SSepherosa Ziehau 					m_new->m_pkthdr.csum_data = 0xffff;
259015516c77SSepherosa Ziehau 				}
259115516c77SSepherosa Ziehau 			} else if (pr != IPPROTO_DONE && do_csum &&
259215516c77SSepherosa Ziehau 			    (rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) {
259315516c77SSepherosa Ziehau 				rxr->hn_csum_trusted++;
259415516c77SSepherosa Ziehau 				m_new->m_pkthdr.csum_flags |=
259515516c77SSepherosa Ziehau 				    (CSUM_IP_CHECKED | CSUM_IP_VALID);
259615516c77SSepherosa Ziehau 			}
259715516c77SSepherosa Ziehau 		}
259815516c77SSepherosa Ziehau 	}
259915516c77SSepherosa Ziehau skip:
260015516c77SSepherosa Ziehau 	if (info->vlan_info != HN_NDIS_VLAN_INFO_INVALID) {
260115516c77SSepherosa Ziehau 		m_new->m_pkthdr.ether_vtag = EVL_MAKETAG(
260215516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_ID(info->vlan_info),
260315516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_PRI(info->vlan_info),
260415516c77SSepherosa Ziehau 		    NDIS_VLAN_INFO_CFI(info->vlan_info));
260515516c77SSepherosa Ziehau 		m_new->m_flags |= M_VLANTAG;
260615516c77SSepherosa Ziehau 	}
260715516c77SSepherosa Ziehau 
260815516c77SSepherosa Ziehau 	if (info->hash_info != HN_NDIS_HASH_INFO_INVALID) {
260915516c77SSepherosa Ziehau 		rxr->hn_rss_pkts++;
261015516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = info->hash_value;
261115516c77SSepherosa Ziehau 		hash_type = M_HASHTYPE_OPAQUE_HASH;
261215516c77SSepherosa Ziehau 		if ((info->hash_info & NDIS_HASH_FUNCTION_MASK) ==
261315516c77SSepherosa Ziehau 		    NDIS_HASH_FUNCTION_TOEPLITZ) {
261415516c77SSepherosa Ziehau 			uint32_t type = (info->hash_info & NDIS_HASH_TYPE_MASK);
261515516c77SSepherosa Ziehau 
261615516c77SSepherosa Ziehau 			/*
261715516c77SSepherosa Ziehau 			 * NOTE:
261815516c77SSepherosa Ziehau 			 * do_lro is resetted, if the hash types are not TCP
261915516c77SSepherosa Ziehau 			 * related.  See the comment in the above csum_flags
262015516c77SSepherosa Ziehau 			 * setup section.
262115516c77SSepherosa Ziehau 			 */
262215516c77SSepherosa Ziehau 			switch (type) {
262315516c77SSepherosa Ziehau 			case NDIS_HASH_IPV4:
262415516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV4;
262515516c77SSepherosa Ziehau 				do_lro = 0;
262615516c77SSepherosa Ziehau 				break;
262715516c77SSepherosa Ziehau 
262815516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV4:
262915516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV4;
263015516c77SSepherosa Ziehau 				break;
263115516c77SSepherosa Ziehau 
263215516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6:
263315516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6;
263415516c77SSepherosa Ziehau 				do_lro = 0;
263515516c77SSepherosa Ziehau 				break;
263615516c77SSepherosa Ziehau 
263715516c77SSepherosa Ziehau 			case NDIS_HASH_IPV6_EX:
263815516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_IPV6_EX;
263915516c77SSepherosa Ziehau 				do_lro = 0;
264015516c77SSepherosa Ziehau 				break;
264115516c77SSepherosa Ziehau 
264215516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6:
264315516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6;
264415516c77SSepherosa Ziehau 				break;
264515516c77SSepherosa Ziehau 
264615516c77SSepherosa Ziehau 			case NDIS_HASH_TCP_IPV6_EX:
264715516c77SSepherosa Ziehau 				hash_type = M_HASHTYPE_RSS_TCP_IPV6_EX;
264815516c77SSepherosa Ziehau 				break;
264915516c77SSepherosa Ziehau 			}
265015516c77SSepherosa Ziehau 		}
265115516c77SSepherosa Ziehau 	} else {
265215516c77SSepherosa Ziehau 		m_new->m_pkthdr.flowid = rxr->hn_rx_idx;
265315516c77SSepherosa Ziehau 		hash_type = M_HASHTYPE_OPAQUE;
265415516c77SSepherosa Ziehau 	}
265515516c77SSepherosa Ziehau 	M_HASHTYPE_SET(m_new, hash_type);
265615516c77SSepherosa Ziehau 
265715516c77SSepherosa Ziehau 	/*
265815516c77SSepherosa Ziehau 	 * Note:  Moved RX completion back to hv_nv_on_receive() so all
265915516c77SSepherosa Ziehau 	 * messages (not just data messages) will trigger a response.
266015516c77SSepherosa Ziehau 	 */
266115516c77SSepherosa Ziehau 
266215516c77SSepherosa Ziehau 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
266315516c77SSepherosa Ziehau 	rxr->hn_pkts++;
266415516c77SSepherosa Ziehau 
266515516c77SSepherosa Ziehau 	if ((ifp->if_capenable & IFCAP_LRO) && do_lro) {
266615516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
266715516c77SSepherosa Ziehau 		struct lro_ctrl *lro = &rxr->hn_lro;
266815516c77SSepherosa Ziehau 
266915516c77SSepherosa Ziehau 		if (lro->lro_cnt) {
267015516c77SSepherosa Ziehau 			rxr->hn_lro_tried++;
267115516c77SSepherosa Ziehau 			if (hn_lro_rx(lro, m_new) == 0) {
267215516c77SSepherosa Ziehau 				/* DONE! */
267315516c77SSepherosa Ziehau 				return 0;
267415516c77SSepherosa Ziehau 			}
267515516c77SSepherosa Ziehau 		}
267615516c77SSepherosa Ziehau #endif
267715516c77SSepherosa Ziehau 	}
267815516c77SSepherosa Ziehau 
267915516c77SSepherosa Ziehau 	/* We're not holding the lock here, so don't release it */
268015516c77SSepherosa Ziehau 	(*ifp->if_input)(ifp, m_new);
268115516c77SSepherosa Ziehau 
268215516c77SSepherosa Ziehau 	return (0);
268315516c77SSepherosa Ziehau }
268415516c77SSepherosa Ziehau 
268515516c77SSepherosa Ziehau static int
268615516c77SSepherosa Ziehau hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
268715516c77SSepherosa Ziehau {
268815516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
268915516c77SSepherosa Ziehau 	struct ifreq *ifr = (struct ifreq *)data;
269015516c77SSepherosa Ziehau 	int mask, error = 0;
269115516c77SSepherosa Ziehau 
269215516c77SSepherosa Ziehau 	switch (cmd) {
269315516c77SSepherosa Ziehau 	case SIOCSIFMTU:
269415516c77SSepherosa Ziehau 		if (ifr->ifr_mtu > HN_MTU_MAX) {
269515516c77SSepherosa Ziehau 			error = EINVAL;
269615516c77SSepherosa Ziehau 			break;
269715516c77SSepherosa Ziehau 		}
269815516c77SSepherosa Ziehau 
269915516c77SSepherosa Ziehau 		HN_LOCK(sc);
270015516c77SSepherosa Ziehau 
270115516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
270215516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
270315516c77SSepherosa Ziehau 			break;
270415516c77SSepherosa Ziehau 		}
270515516c77SSepherosa Ziehau 
270615516c77SSepherosa Ziehau 		if ((sc->hn_caps & HN_CAP_MTU) == 0) {
270715516c77SSepherosa Ziehau 			/* Can't change MTU */
270815516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
270915516c77SSepherosa Ziehau 			error = EOPNOTSUPP;
271015516c77SSepherosa Ziehau 			break;
271115516c77SSepherosa Ziehau 		}
271215516c77SSepherosa Ziehau 
271315516c77SSepherosa Ziehau 		if (ifp->if_mtu == ifr->ifr_mtu) {
271415516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
271515516c77SSepherosa Ziehau 			break;
271615516c77SSepherosa Ziehau 		}
271715516c77SSepherosa Ziehau 
271815516c77SSepherosa Ziehau 		/*
271915516c77SSepherosa Ziehau 		 * Suspend this interface before the synthetic parts
272015516c77SSepherosa Ziehau 		 * are ripped.
272115516c77SSepherosa Ziehau 		 */
272215516c77SSepherosa Ziehau 		hn_suspend(sc);
272315516c77SSepherosa Ziehau 
272415516c77SSepherosa Ziehau 		/*
272515516c77SSepherosa Ziehau 		 * Detach the synthetics parts, i.e. NVS and RNDIS.
272615516c77SSepherosa Ziehau 		 */
272715516c77SSepherosa Ziehau 		hn_synth_detach(sc);
272815516c77SSepherosa Ziehau 
272915516c77SSepherosa Ziehau 		/*
273015516c77SSepherosa Ziehau 		 * Reattach the synthetic parts, i.e. NVS and RNDIS,
273115516c77SSepherosa Ziehau 		 * with the new MTU setting.
273215516c77SSepherosa Ziehau 		 */
273315516c77SSepherosa Ziehau 		error = hn_synth_attach(sc, ifr->ifr_mtu);
273415516c77SSepherosa Ziehau 		if (error) {
273515516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
273615516c77SSepherosa Ziehau 			break;
273715516c77SSepherosa Ziehau 		}
273815516c77SSepherosa Ziehau 
273915516c77SSepherosa Ziehau 		/*
274015516c77SSepherosa Ziehau 		 * Commit the requested MTU, after the synthetic parts
274115516c77SSepherosa Ziehau 		 * have been successfully attached.
274215516c77SSepherosa Ziehau 		 */
274315516c77SSepherosa Ziehau 		ifp->if_mtu = ifr->ifr_mtu;
274415516c77SSepherosa Ziehau 
274515516c77SSepherosa Ziehau 		/*
274615516c77SSepherosa Ziehau 		 * Make sure that various parameters based on MTU are
274715516c77SSepherosa Ziehau 		 * still valid, after the MTU change.
274815516c77SSepherosa Ziehau 		 */
274915516c77SSepherosa Ziehau 		if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
275015516c77SSepherosa Ziehau 			hn_set_chim_size(sc, sc->hn_chim_szmax);
275115516c77SSepherosa Ziehau 		hn_set_tso_maxsize(sc, hn_tso_maxlen, ifp->if_mtu);
275215516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
275315516c77SSepherosa Ziehau 		if (sc->hn_rx_ring[0].hn_lro.lro_length_lim <
275415516c77SSepherosa Ziehau 		    HN_LRO_LENLIM_MIN(ifp))
275515516c77SSepherosa Ziehau 			hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp));
275615516c77SSepherosa Ziehau #endif
275715516c77SSepherosa Ziehau 
275815516c77SSepherosa Ziehau 		/*
275915516c77SSepherosa Ziehau 		 * All done!  Resume the interface now.
276015516c77SSepherosa Ziehau 		 */
276115516c77SSepherosa Ziehau 		hn_resume(sc);
276215516c77SSepherosa Ziehau 
276315516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
276415516c77SSepherosa Ziehau 		break;
276515516c77SSepherosa Ziehau 
276615516c77SSepherosa Ziehau 	case SIOCSIFFLAGS:
276715516c77SSepherosa Ziehau 		HN_LOCK(sc);
276815516c77SSepherosa Ziehau 
276915516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
277015516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
277115516c77SSepherosa Ziehau 			break;
277215516c77SSepherosa Ziehau 		}
277315516c77SSepherosa Ziehau 
277415516c77SSepherosa Ziehau 		if (ifp->if_flags & IFF_UP) {
2775fdc4f478SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
2776fdc4f478SSepherosa Ziehau 				/*
2777fdc4f478SSepherosa Ziehau 				 * Caller meight hold mutex, e.g.
2778fdc4f478SSepherosa Ziehau 				 * bpf; use busy-wait for the RNDIS
2779fdc4f478SSepherosa Ziehau 				 * reply.
2780fdc4f478SSepherosa Ziehau 				 */
2781fdc4f478SSepherosa Ziehau 				HN_NO_SLEEPING(sc);
2782c08f7b2cSSepherosa Ziehau 				hn_rxfilter_config(sc);
2783fdc4f478SSepherosa Ziehau 				HN_SLEEPING_OK(sc);
2784fdc4f478SSepherosa Ziehau 			} else {
278515516c77SSepherosa Ziehau 				hn_init_locked(sc);
2786fdc4f478SSepherosa Ziehau 			}
278715516c77SSepherosa Ziehau 		} else {
278815516c77SSepherosa Ziehau 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
27895bdfd3fdSDexuan Cui 				hn_stop(sc, false);
279015516c77SSepherosa Ziehau 		}
279115516c77SSepherosa Ziehau 		sc->hn_if_flags = ifp->if_flags;
279215516c77SSepherosa Ziehau 
279315516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
279415516c77SSepherosa Ziehau 		break;
279515516c77SSepherosa Ziehau 
279615516c77SSepherosa Ziehau 	case SIOCSIFCAP:
279715516c77SSepherosa Ziehau 		HN_LOCK(sc);
279815516c77SSepherosa Ziehau 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
279915516c77SSepherosa Ziehau 
280015516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM) {
280115516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM;
280215516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM)
280315516c77SSepherosa Ziehau 				ifp->if_hwassist |= HN_CSUM_IP_HWASSIST(sc);
280415516c77SSepherosa Ziehau 			else
280515516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~HN_CSUM_IP_HWASSIST(sc);
280615516c77SSepherosa Ziehau 		}
280715516c77SSepherosa Ziehau 		if (mask & IFCAP_TXCSUM_IPV6) {
280815516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TXCSUM_IPV6;
280915516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TXCSUM_IPV6)
281015516c77SSepherosa Ziehau 				ifp->if_hwassist |= HN_CSUM_IP6_HWASSIST(sc);
281115516c77SSepherosa Ziehau 			else
281215516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~HN_CSUM_IP6_HWASSIST(sc);
281315516c77SSepherosa Ziehau 		}
281415516c77SSepherosa Ziehau 
281515516c77SSepherosa Ziehau 		/* TODO: flip RNDIS offload parameters for RXCSUM. */
281615516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM)
281715516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM;
281815516c77SSepherosa Ziehau #ifdef foo
281915516c77SSepherosa Ziehau 		/* We can't diff IPv6 packets from IPv4 packets on RX path. */
282015516c77SSepherosa Ziehau 		if (mask & IFCAP_RXCSUM_IPV6)
282115516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_RXCSUM_IPV6;
282215516c77SSepherosa Ziehau #endif
282315516c77SSepherosa Ziehau 
282415516c77SSepherosa Ziehau 		if (mask & IFCAP_LRO)
282515516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_LRO;
282615516c77SSepherosa Ziehau 
282715516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO4) {
282815516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO4;
282915516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO4)
283015516c77SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_IP_TSO;
283115516c77SSepherosa Ziehau 			else
283215516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_IP_TSO;
283315516c77SSepherosa Ziehau 		}
283415516c77SSepherosa Ziehau 		if (mask & IFCAP_TSO6) {
283515516c77SSepherosa Ziehau 			ifp->if_capenable ^= IFCAP_TSO6;
283615516c77SSepherosa Ziehau 			if (ifp->if_capenable & IFCAP_TSO6)
283715516c77SSepherosa Ziehau 				ifp->if_hwassist |= CSUM_IP6_TSO;
283815516c77SSepherosa Ziehau 			else
283915516c77SSepherosa Ziehau 				ifp->if_hwassist &= ~CSUM_IP6_TSO;
284015516c77SSepherosa Ziehau 		}
284115516c77SSepherosa Ziehau 
284215516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
284315516c77SSepherosa Ziehau 		break;
284415516c77SSepherosa Ziehau 
284515516c77SSepherosa Ziehau 	case SIOCADDMULTI:
284615516c77SSepherosa Ziehau 	case SIOCDELMULTI:
284715516c77SSepherosa Ziehau 		HN_LOCK(sc);
284815516c77SSepherosa Ziehau 
284915516c77SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) {
285015516c77SSepherosa Ziehau 			HN_UNLOCK(sc);
285115516c77SSepherosa Ziehau 			break;
285215516c77SSepherosa Ziehau 		}
2853fdc4f478SSepherosa Ziehau 		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
2854fdc4f478SSepherosa Ziehau 			/*
2855fdc4f478SSepherosa Ziehau 			 * Multicast uses mutex; use busy-wait for
2856fdc4f478SSepherosa Ziehau 			 * the RNDIS reply.
2857fdc4f478SSepherosa Ziehau 			 */
2858fdc4f478SSepherosa Ziehau 			HN_NO_SLEEPING(sc);
2859c08f7b2cSSepherosa Ziehau 			hn_rxfilter_config(sc);
2860fdc4f478SSepherosa Ziehau 			HN_SLEEPING_OK(sc);
2861fdc4f478SSepherosa Ziehau 		}
286215516c77SSepherosa Ziehau 
286315516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
286415516c77SSepherosa Ziehau 		break;
286515516c77SSepherosa Ziehau 
286615516c77SSepherosa Ziehau 	case SIOCSIFMEDIA:
286715516c77SSepherosa Ziehau 	case SIOCGIFMEDIA:
286815516c77SSepherosa Ziehau 		error = ifmedia_ioctl(ifp, ifr, &sc->hn_media, cmd);
286915516c77SSepherosa Ziehau 		break;
287015516c77SSepherosa Ziehau 
287115516c77SSepherosa Ziehau 	default:
287215516c77SSepherosa Ziehau 		error = ether_ioctl(ifp, cmd, data);
287315516c77SSepherosa Ziehau 		break;
287415516c77SSepherosa Ziehau 	}
287515516c77SSepherosa Ziehau 	return (error);
287615516c77SSepherosa Ziehau }
287715516c77SSepherosa Ziehau 
287815516c77SSepherosa Ziehau static void
28795bdfd3fdSDexuan Cui hn_stop(struct hn_softc *sc, bool detaching)
288015516c77SSepherosa Ziehau {
288115516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
288215516c77SSepherosa Ziehau 	int i;
288315516c77SSepherosa Ziehau 
288415516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
288515516c77SSepherosa Ziehau 
288615516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
288715516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
288815516c77SSepherosa Ziehau 
28896c1204dfSSepherosa Ziehau 	/* Disable polling. */
28906c1204dfSSepherosa Ziehau 	hn_polling(sc, 0);
28916c1204dfSSepherosa Ziehau 
289215516c77SSepherosa Ziehau 	/* Clear RUNNING bit _before_ hn_suspend_data() */
289315516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
289415516c77SSepherosa Ziehau 	hn_suspend_data(sc);
289515516c77SSepherosa Ziehau 
289615516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
289715516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
289815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
289915516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
29005bdfd3fdSDexuan Cui 
29015bdfd3fdSDexuan Cui 	/*
29025bdfd3fdSDexuan Cui 	 * If the VF is active, make sure the filter is not 0, even if
29035bdfd3fdSDexuan Cui 	 * the synthetic NIC is down.
29045bdfd3fdSDexuan Cui 	 */
29055bdfd3fdSDexuan Cui 	if (!detaching && (sc->hn_flags & HN_FLAG_VF))
29065bdfd3fdSDexuan Cui 		hn_rxfilter_config(sc);
290715516c77SSepherosa Ziehau }
290815516c77SSepherosa Ziehau 
290915516c77SSepherosa Ziehau static void
291015516c77SSepherosa Ziehau hn_init_locked(struct hn_softc *sc)
291115516c77SSepherosa Ziehau {
291215516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
291315516c77SSepherosa Ziehau 	int i;
291415516c77SSepherosa Ziehau 
291515516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
291615516c77SSepherosa Ziehau 
291715516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0)
291815516c77SSepherosa Ziehau 		return;
291915516c77SSepherosa Ziehau 
292015516c77SSepherosa Ziehau 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
292115516c77SSepherosa Ziehau 		return;
292215516c77SSepherosa Ziehau 
292315516c77SSepherosa Ziehau 	/* Configure RX filter */
2924c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
292515516c77SSepherosa Ziehau 
292615516c77SSepherosa Ziehau 	/* Clear OACTIVE bit. */
292715516c77SSepherosa Ziehau 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
292815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
292915516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_oactive = 0;
293015516c77SSepherosa Ziehau 
293115516c77SSepherosa Ziehau 	/* Clear TX 'suspended' bit. */
293215516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_inuse);
293315516c77SSepherosa Ziehau 
293415516c77SSepherosa Ziehau 	/* Everything is ready; unleash! */
293515516c77SSepherosa Ziehau 	atomic_set_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
29366c1204dfSSepherosa Ziehau 
29376c1204dfSSepherosa Ziehau 	/* Re-enable polling if requested. */
29386c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz > 0)
29396c1204dfSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
294015516c77SSepherosa Ziehau }
294115516c77SSepherosa Ziehau 
294215516c77SSepherosa Ziehau static void
294315516c77SSepherosa Ziehau hn_init(void *xsc)
294415516c77SSepherosa Ziehau {
294515516c77SSepherosa Ziehau 	struct hn_softc *sc = xsc;
294615516c77SSepherosa Ziehau 
294715516c77SSepherosa Ziehau 	HN_LOCK(sc);
294815516c77SSepherosa Ziehau 	hn_init_locked(sc);
294915516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
295015516c77SSepherosa Ziehau }
295115516c77SSepherosa Ziehau 
295215516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
295315516c77SSepherosa Ziehau 
295415516c77SSepherosa Ziehau static int
295515516c77SSepherosa Ziehau hn_lro_lenlim_sysctl(SYSCTL_HANDLER_ARGS)
295615516c77SSepherosa Ziehau {
295715516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
295815516c77SSepherosa Ziehau 	unsigned int lenlim;
295915516c77SSepherosa Ziehau 	int error;
296015516c77SSepherosa Ziehau 
296115516c77SSepherosa Ziehau 	lenlim = sc->hn_rx_ring[0].hn_lro.lro_length_lim;
296215516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &lenlim, 0, req);
296315516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
296415516c77SSepherosa Ziehau 		return error;
296515516c77SSepherosa Ziehau 
296615516c77SSepherosa Ziehau 	HN_LOCK(sc);
296715516c77SSepherosa Ziehau 	if (lenlim < HN_LRO_LENLIM_MIN(sc->hn_ifp) ||
296815516c77SSepherosa Ziehau 	    lenlim > TCP_LRO_LENGTH_MAX) {
296915516c77SSepherosa Ziehau 		HN_UNLOCK(sc);
297015516c77SSepherosa Ziehau 		return EINVAL;
297115516c77SSepherosa Ziehau 	}
297215516c77SSepherosa Ziehau 	hn_set_lro_lenlim(sc, lenlim);
297315516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
297415516c77SSepherosa Ziehau 
297515516c77SSepherosa Ziehau 	return 0;
297615516c77SSepherosa Ziehau }
297715516c77SSepherosa Ziehau 
297815516c77SSepherosa Ziehau static int
297915516c77SSepherosa Ziehau hn_lro_ackcnt_sysctl(SYSCTL_HANDLER_ARGS)
298015516c77SSepherosa Ziehau {
298115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
298215516c77SSepherosa Ziehau 	int ackcnt, error, i;
298315516c77SSepherosa Ziehau 
298415516c77SSepherosa Ziehau 	/*
298515516c77SSepherosa Ziehau 	 * lro_ackcnt_lim is append count limit,
298615516c77SSepherosa Ziehau 	 * +1 to turn it into aggregation limit.
298715516c77SSepherosa Ziehau 	 */
298815516c77SSepherosa Ziehau 	ackcnt = sc->hn_rx_ring[0].hn_lro.lro_ackcnt_lim + 1;
298915516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &ackcnt, 0, req);
299015516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
299115516c77SSepherosa Ziehau 		return error;
299215516c77SSepherosa Ziehau 
299315516c77SSepherosa Ziehau 	if (ackcnt < 2 || ackcnt > (TCP_LRO_ACKCNT_MAX + 1))
299415516c77SSepherosa Ziehau 		return EINVAL;
299515516c77SSepherosa Ziehau 
299615516c77SSepherosa Ziehau 	/*
299715516c77SSepherosa Ziehau 	 * Convert aggregation limit back to append
299815516c77SSepherosa Ziehau 	 * count limit.
299915516c77SSepherosa Ziehau 	 */
300015516c77SSepherosa Ziehau 	--ackcnt;
300115516c77SSepherosa Ziehau 	HN_LOCK(sc);
3002a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
300315516c77SSepherosa Ziehau 		sc->hn_rx_ring[i].hn_lro.lro_ackcnt_lim = ackcnt;
300415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
300515516c77SSepherosa Ziehau 	return 0;
300615516c77SSepherosa Ziehau }
300715516c77SSepherosa Ziehau 
300815516c77SSepherosa Ziehau #endif
300915516c77SSepherosa Ziehau 
301015516c77SSepherosa Ziehau static int
301115516c77SSepherosa Ziehau hn_trust_hcsum_sysctl(SYSCTL_HANDLER_ARGS)
301215516c77SSepherosa Ziehau {
301315516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
301415516c77SSepherosa Ziehau 	int hcsum = arg2;
301515516c77SSepherosa Ziehau 	int on, error, i;
301615516c77SSepherosa Ziehau 
301715516c77SSepherosa Ziehau 	on = 0;
301815516c77SSepherosa Ziehau 	if (sc->hn_rx_ring[0].hn_trust_hcsum & hcsum)
301915516c77SSepherosa Ziehau 		on = 1;
302015516c77SSepherosa Ziehau 
302115516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &on, 0, req);
302215516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
302315516c77SSepherosa Ziehau 		return error;
302415516c77SSepherosa Ziehau 
302515516c77SSepherosa Ziehau 	HN_LOCK(sc);
3026a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
302715516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
302815516c77SSepherosa Ziehau 
302915516c77SSepherosa Ziehau 		if (on)
303015516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= hcsum;
303115516c77SSepherosa Ziehau 		else
303215516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum &= ~hcsum;
303315516c77SSepherosa Ziehau 	}
303415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
303515516c77SSepherosa Ziehau 	return 0;
303615516c77SSepherosa Ziehau }
303715516c77SSepherosa Ziehau 
303815516c77SSepherosa Ziehau static int
303915516c77SSepherosa Ziehau hn_chim_size_sysctl(SYSCTL_HANDLER_ARGS)
304015516c77SSepherosa Ziehau {
304115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
304215516c77SSepherosa Ziehau 	int chim_size, error;
304315516c77SSepherosa Ziehau 
304415516c77SSepherosa Ziehau 	chim_size = sc->hn_tx_ring[0].hn_chim_size;
304515516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &chim_size, 0, req);
304615516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
304715516c77SSepherosa Ziehau 		return error;
304815516c77SSepherosa Ziehau 
304915516c77SSepherosa Ziehau 	if (chim_size > sc->hn_chim_szmax || chim_size <= 0)
305015516c77SSepherosa Ziehau 		return EINVAL;
305115516c77SSepherosa Ziehau 
305215516c77SSepherosa Ziehau 	HN_LOCK(sc);
305315516c77SSepherosa Ziehau 	hn_set_chim_size(sc, chim_size);
305415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
305515516c77SSepherosa Ziehau 	return 0;
305615516c77SSepherosa Ziehau }
305715516c77SSepherosa Ziehau 
305815516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
305915516c77SSepherosa Ziehau static int
306015516c77SSepherosa Ziehau hn_rx_stat_int_sysctl(SYSCTL_HANDLER_ARGS)
306115516c77SSepherosa Ziehau {
306215516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
306315516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
306415516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
306515516c77SSepherosa Ziehau 	uint64_t stat;
306615516c77SSepherosa Ziehau 
306715516c77SSepherosa Ziehau 	stat = 0;
306815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
306915516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
307015516c77SSepherosa Ziehau 		stat += *((int *)((uint8_t *)rxr + ofs));
307115516c77SSepherosa Ziehau 	}
307215516c77SSepherosa Ziehau 
307315516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
307415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
307515516c77SSepherosa Ziehau 		return error;
307615516c77SSepherosa Ziehau 
307715516c77SSepherosa Ziehau 	/* Zero out this stat. */
307815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
307915516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
308015516c77SSepherosa Ziehau 		*((int *)((uint8_t *)rxr + ofs)) = 0;
308115516c77SSepherosa Ziehau 	}
308215516c77SSepherosa Ziehau 	return 0;
308315516c77SSepherosa Ziehau }
308415516c77SSepherosa Ziehau #else
308515516c77SSepherosa Ziehau static int
308615516c77SSepherosa Ziehau hn_rx_stat_u64_sysctl(SYSCTL_HANDLER_ARGS)
308715516c77SSepherosa Ziehau {
308815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
308915516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
309015516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
309115516c77SSepherosa Ziehau 	uint64_t stat;
309215516c77SSepherosa Ziehau 
309315516c77SSepherosa Ziehau 	stat = 0;
3094a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
309515516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
309615516c77SSepherosa Ziehau 		stat += *((uint64_t *)((uint8_t *)rxr + ofs));
309715516c77SSepherosa Ziehau 	}
309815516c77SSepherosa Ziehau 
309915516c77SSepherosa Ziehau 	error = sysctl_handle_64(oidp, &stat, 0, req);
310015516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
310115516c77SSepherosa Ziehau 		return error;
310215516c77SSepherosa Ziehau 
310315516c77SSepherosa Ziehau 	/* Zero out this stat. */
3104a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
310515516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
310615516c77SSepherosa Ziehau 		*((uint64_t *)((uint8_t *)rxr + ofs)) = 0;
310715516c77SSepherosa Ziehau 	}
310815516c77SSepherosa Ziehau 	return 0;
310915516c77SSepherosa Ziehau }
311015516c77SSepherosa Ziehau 
311115516c77SSepherosa Ziehau #endif
311215516c77SSepherosa Ziehau 
311315516c77SSepherosa Ziehau static int
311415516c77SSepherosa Ziehau hn_rx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
311515516c77SSepherosa Ziehau {
311615516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
311715516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
311815516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
311915516c77SSepherosa Ziehau 	u_long stat;
312015516c77SSepherosa Ziehau 
312115516c77SSepherosa Ziehau 	stat = 0;
3122a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
312315516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
312415516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)rxr + ofs));
312515516c77SSepherosa Ziehau 	}
312615516c77SSepherosa Ziehau 
312715516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
312815516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
312915516c77SSepherosa Ziehau 		return error;
313015516c77SSepherosa Ziehau 
313115516c77SSepherosa Ziehau 	/* Zero out this stat. */
3132a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
313315516c77SSepherosa Ziehau 		rxr = &sc->hn_rx_ring[i];
313415516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)rxr + ofs)) = 0;
313515516c77SSepherosa Ziehau 	}
313615516c77SSepherosa Ziehau 	return 0;
313715516c77SSepherosa Ziehau }
313815516c77SSepherosa Ziehau 
313915516c77SSepherosa Ziehau static int
314015516c77SSepherosa Ziehau hn_tx_stat_ulong_sysctl(SYSCTL_HANDLER_ARGS)
314115516c77SSepherosa Ziehau {
314215516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
314315516c77SSepherosa Ziehau 	int ofs = arg2, i, error;
314415516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
314515516c77SSepherosa Ziehau 	u_long stat;
314615516c77SSepherosa Ziehau 
314715516c77SSepherosa Ziehau 	stat = 0;
3148a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
314915516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
315015516c77SSepherosa Ziehau 		stat += *((u_long *)((uint8_t *)txr + ofs));
315115516c77SSepherosa Ziehau 	}
315215516c77SSepherosa Ziehau 
315315516c77SSepherosa Ziehau 	error = sysctl_handle_long(oidp, &stat, 0, req);
315415516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
315515516c77SSepherosa Ziehau 		return error;
315615516c77SSepherosa Ziehau 
315715516c77SSepherosa Ziehau 	/* Zero out this stat. */
3158a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
315915516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
316015516c77SSepherosa Ziehau 		*((u_long *)((uint8_t *)txr + ofs)) = 0;
316115516c77SSepherosa Ziehau 	}
316215516c77SSepherosa Ziehau 	return 0;
316315516c77SSepherosa Ziehau }
316415516c77SSepherosa Ziehau 
316515516c77SSepherosa Ziehau static int
316615516c77SSepherosa Ziehau hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS)
316715516c77SSepherosa Ziehau {
316815516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
316915516c77SSepherosa Ziehau 	int ofs = arg2, i, error, conf;
317015516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
317115516c77SSepherosa Ziehau 
317215516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[0];
317315516c77SSepherosa Ziehau 	conf = *((int *)((uint8_t *)txr + ofs));
317415516c77SSepherosa Ziehau 
317515516c77SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &conf, 0, req);
317615516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
317715516c77SSepherosa Ziehau 		return error;
317815516c77SSepherosa Ziehau 
317915516c77SSepherosa Ziehau 	HN_LOCK(sc);
3180a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
318115516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
318215516c77SSepherosa Ziehau 		*((int *)((uint8_t *)txr + ofs)) = conf;
318315516c77SSepherosa Ziehau 	}
318415516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
318515516c77SSepherosa Ziehau 
318615516c77SSepherosa Ziehau 	return 0;
318715516c77SSepherosa Ziehau }
318815516c77SSepherosa Ziehau 
318915516c77SSepherosa Ziehau static int
3190dc13fee6SSepherosa Ziehau hn_txagg_size_sysctl(SYSCTL_HANDLER_ARGS)
3191dc13fee6SSepherosa Ziehau {
3192dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3193dc13fee6SSepherosa Ziehau 	int error, size;
3194dc13fee6SSepherosa Ziehau 
3195dc13fee6SSepherosa Ziehau 	size = sc->hn_agg_size;
3196dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &size, 0, req);
3197dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
3198dc13fee6SSepherosa Ziehau 		return (error);
3199dc13fee6SSepherosa Ziehau 
3200dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
3201dc13fee6SSepherosa Ziehau 	sc->hn_agg_size = size;
3202dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
3203dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
3204dc13fee6SSepherosa Ziehau 
3205dc13fee6SSepherosa Ziehau 	return (0);
3206dc13fee6SSepherosa Ziehau }
3207dc13fee6SSepherosa Ziehau 
3208dc13fee6SSepherosa Ziehau static int
3209dc13fee6SSepherosa Ziehau hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS)
3210dc13fee6SSepherosa Ziehau {
3211dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3212dc13fee6SSepherosa Ziehau 	int error, pkts;
3213dc13fee6SSepherosa Ziehau 
3214dc13fee6SSepherosa Ziehau 	pkts = sc->hn_agg_pkts;
3215dc13fee6SSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pkts, 0, req);
3216dc13fee6SSepherosa Ziehau 	if (error || req->newptr == NULL)
3217dc13fee6SSepherosa Ziehau 		return (error);
3218dc13fee6SSepherosa Ziehau 
3219dc13fee6SSepherosa Ziehau 	HN_LOCK(sc);
3220dc13fee6SSepherosa Ziehau 	sc->hn_agg_pkts = pkts;
3221dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
3222dc13fee6SSepherosa Ziehau 	HN_UNLOCK(sc);
3223dc13fee6SSepherosa Ziehau 
3224dc13fee6SSepherosa Ziehau 	return (0);
3225dc13fee6SSepherosa Ziehau }
3226dc13fee6SSepherosa Ziehau 
3227dc13fee6SSepherosa Ziehau static int
3228dc13fee6SSepherosa Ziehau hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS)
3229dc13fee6SSepherosa Ziehau {
3230dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3231dc13fee6SSepherosa Ziehau 	int pkts;
3232dc13fee6SSepherosa Ziehau 
3233dc13fee6SSepherosa Ziehau 	pkts = sc->hn_tx_ring[0].hn_agg_pktmax;
3234dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &pkts, 0, req));
3235dc13fee6SSepherosa Ziehau }
3236dc13fee6SSepherosa Ziehau 
3237dc13fee6SSepherosa Ziehau static int
3238dc13fee6SSepherosa Ziehau hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS)
3239dc13fee6SSepherosa Ziehau {
3240dc13fee6SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3241dc13fee6SSepherosa Ziehau 	int align;
3242dc13fee6SSepherosa Ziehau 
3243dc13fee6SSepherosa Ziehau 	align = sc->hn_tx_ring[0].hn_agg_align;
3244dc13fee6SSepherosa Ziehau 	return (sysctl_handle_int(oidp, &align, 0, req));
3245dc13fee6SSepherosa Ziehau }
3246dc13fee6SSepherosa Ziehau 
32476c1204dfSSepherosa Ziehau static void
32486c1204dfSSepherosa Ziehau hn_chan_polling(struct vmbus_channel *chan, u_int pollhz)
32496c1204dfSSepherosa Ziehau {
32506c1204dfSSepherosa Ziehau 	if (pollhz == 0)
32516c1204dfSSepherosa Ziehau 		vmbus_chan_poll_disable(chan);
32526c1204dfSSepherosa Ziehau 	else
32536c1204dfSSepherosa Ziehau 		vmbus_chan_poll_enable(chan, pollhz);
32546c1204dfSSepherosa Ziehau }
32556c1204dfSSepherosa Ziehau 
32566c1204dfSSepherosa Ziehau static void
32576c1204dfSSepherosa Ziehau hn_polling(struct hn_softc *sc, u_int pollhz)
32586c1204dfSSepherosa Ziehau {
32596c1204dfSSepherosa Ziehau 	int nsubch = sc->hn_rx_ring_inuse - 1;
32606c1204dfSSepherosa Ziehau 
32616c1204dfSSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
32626c1204dfSSepherosa Ziehau 
32636c1204dfSSepherosa Ziehau 	if (nsubch > 0) {
32646c1204dfSSepherosa Ziehau 		struct vmbus_channel **subch;
32656c1204dfSSepherosa Ziehau 		int i;
32666c1204dfSSepherosa Ziehau 
32676c1204dfSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
32686c1204dfSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
32696c1204dfSSepherosa Ziehau 			hn_chan_polling(subch[i], pollhz);
32706c1204dfSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
32716c1204dfSSepherosa Ziehau 	}
32726c1204dfSSepherosa Ziehau 	hn_chan_polling(sc->hn_prichan, pollhz);
32736c1204dfSSepherosa Ziehau }
32746c1204dfSSepherosa Ziehau 
32756c1204dfSSepherosa Ziehau static int
32766c1204dfSSepherosa Ziehau hn_polling_sysctl(SYSCTL_HANDLER_ARGS)
32776c1204dfSSepherosa Ziehau {
32786c1204dfSSepherosa Ziehau 	struct hn_softc *sc = arg1;
32796c1204dfSSepherosa Ziehau 	int pollhz, error;
32806c1204dfSSepherosa Ziehau 
32816c1204dfSSepherosa Ziehau 	pollhz = sc->hn_pollhz;
32826c1204dfSSepherosa Ziehau 	error = sysctl_handle_int(oidp, &pollhz, 0, req);
32836c1204dfSSepherosa Ziehau 	if (error || req->newptr == NULL)
32846c1204dfSSepherosa Ziehau 		return (error);
32856c1204dfSSepherosa Ziehau 
32866c1204dfSSepherosa Ziehau 	if (pollhz != 0 &&
32876c1204dfSSepherosa Ziehau 	    (pollhz < VMBUS_CHAN_POLLHZ_MIN || pollhz > VMBUS_CHAN_POLLHZ_MAX))
32886c1204dfSSepherosa Ziehau 		return (EINVAL);
32896c1204dfSSepherosa Ziehau 
32906c1204dfSSepherosa Ziehau 	HN_LOCK(sc);
32916c1204dfSSepherosa Ziehau 	if (sc->hn_pollhz != pollhz) {
32926c1204dfSSepherosa Ziehau 		sc->hn_pollhz = pollhz;
32936c1204dfSSepherosa Ziehau 		if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) &&
32946c1204dfSSepherosa Ziehau 		    (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
32956c1204dfSSepherosa Ziehau 			hn_polling(sc, sc->hn_pollhz);
32966c1204dfSSepherosa Ziehau 	}
32976c1204dfSSepherosa Ziehau 	HN_UNLOCK(sc);
32986c1204dfSSepherosa Ziehau 
32996c1204dfSSepherosa Ziehau 	return (0);
33006c1204dfSSepherosa Ziehau }
33016c1204dfSSepherosa Ziehau 
3302dc13fee6SSepherosa Ziehau static int
330315516c77SSepherosa Ziehau hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS)
330415516c77SSepherosa Ziehau {
330515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
330615516c77SSepherosa Ziehau 	char verstr[16];
330715516c77SSepherosa Ziehau 
330815516c77SSepherosa Ziehau 	snprintf(verstr, sizeof(verstr), "%u.%u",
330915516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
331015516c77SSepherosa Ziehau 	    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
331115516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
331215516c77SSepherosa Ziehau }
331315516c77SSepherosa Ziehau 
331415516c77SSepherosa Ziehau static int
331515516c77SSepherosa Ziehau hn_caps_sysctl(SYSCTL_HANDLER_ARGS)
331615516c77SSepherosa Ziehau {
331715516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
331815516c77SSepherosa Ziehau 	char caps_str[128];
331915516c77SSepherosa Ziehau 	uint32_t caps;
332015516c77SSepherosa Ziehau 
332115516c77SSepherosa Ziehau 	HN_LOCK(sc);
332215516c77SSepherosa Ziehau 	caps = sc->hn_caps;
332315516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
332415516c77SSepherosa Ziehau 	snprintf(caps_str, sizeof(caps_str), "%b", caps, HN_CAP_BITS);
332515516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, caps_str, sizeof(caps_str), req);
332615516c77SSepherosa Ziehau }
332715516c77SSepherosa Ziehau 
332815516c77SSepherosa Ziehau static int
332915516c77SSepherosa Ziehau hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS)
333015516c77SSepherosa Ziehau {
333115516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
333215516c77SSepherosa Ziehau 	char assist_str[128];
333315516c77SSepherosa Ziehau 	uint32_t hwassist;
333415516c77SSepherosa Ziehau 
333515516c77SSepherosa Ziehau 	HN_LOCK(sc);
333615516c77SSepherosa Ziehau 	hwassist = sc->hn_ifp->if_hwassist;
333715516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
333815516c77SSepherosa Ziehau 	snprintf(assist_str, sizeof(assist_str), "%b", hwassist, CSUM_BITS);
333915516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, assist_str, sizeof(assist_str), req);
334015516c77SSepherosa Ziehau }
334115516c77SSepherosa Ziehau 
334215516c77SSepherosa Ziehau static int
334315516c77SSepherosa Ziehau hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS)
334415516c77SSepherosa Ziehau {
334515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
334615516c77SSepherosa Ziehau 	char filter_str[128];
334715516c77SSepherosa Ziehau 	uint32_t filter;
334815516c77SSepherosa Ziehau 
334915516c77SSepherosa Ziehau 	HN_LOCK(sc);
335015516c77SSepherosa Ziehau 	filter = sc->hn_rx_filter;
335115516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
335215516c77SSepherosa Ziehau 	snprintf(filter_str, sizeof(filter_str), "%b", filter,
335315516c77SSepherosa Ziehau 	    NDIS_PACKET_TYPES);
335415516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req);
335515516c77SSepherosa Ziehau }
335615516c77SSepherosa Ziehau 
335734d68912SSepherosa Ziehau #ifndef RSS
335834d68912SSepherosa Ziehau 
335915516c77SSepherosa Ziehau static int
336015516c77SSepherosa Ziehau hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS)
336115516c77SSepherosa Ziehau {
336215516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
336315516c77SSepherosa Ziehau 	int error;
336415516c77SSepherosa Ziehau 
336515516c77SSepherosa Ziehau 	HN_LOCK(sc);
336615516c77SSepherosa Ziehau 
336715516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
336815516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
336915516c77SSepherosa Ziehau 		goto back;
337015516c77SSepherosa Ziehau 
337115516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_key, sizeof(sc->hn_rss.rss_key));
337215516c77SSepherosa Ziehau 	if (error)
337315516c77SSepherosa Ziehau 		goto back;
337415516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
337515516c77SSepherosa Ziehau 
337615516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse > 1) {
337715516c77SSepherosa Ziehau 		error = hn_rss_reconfig(sc);
337815516c77SSepherosa Ziehau 	} else {
337915516c77SSepherosa Ziehau 		/* Not RSS capable, at least for now; just save the RSS key. */
338015516c77SSepherosa Ziehau 		error = 0;
338115516c77SSepherosa Ziehau 	}
338215516c77SSepherosa Ziehau back:
338315516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
338415516c77SSepherosa Ziehau 	return (error);
338515516c77SSepherosa Ziehau }
338615516c77SSepherosa Ziehau 
338715516c77SSepherosa Ziehau static int
338815516c77SSepherosa Ziehau hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS)
338915516c77SSepherosa Ziehau {
339015516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
339115516c77SSepherosa Ziehau 	int error;
339215516c77SSepherosa Ziehau 
339315516c77SSepherosa Ziehau 	HN_LOCK(sc);
339415516c77SSepherosa Ziehau 
339515516c77SSepherosa Ziehau 	error = SYSCTL_OUT(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
339615516c77SSepherosa Ziehau 	if (error || req->newptr == NULL)
339715516c77SSepherosa Ziehau 		goto back;
339815516c77SSepherosa Ziehau 
339915516c77SSepherosa Ziehau 	/*
340015516c77SSepherosa Ziehau 	 * Don't allow RSS indirect table change, if this interface is not
340115516c77SSepherosa Ziehau 	 * RSS capable currently.
340215516c77SSepherosa Ziehau 	 */
340315516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse == 1) {
340415516c77SSepherosa Ziehau 		error = EOPNOTSUPP;
340515516c77SSepherosa Ziehau 		goto back;
340615516c77SSepherosa Ziehau 	}
340715516c77SSepherosa Ziehau 
340815516c77SSepherosa Ziehau 	error = SYSCTL_IN(req, sc->hn_rss.rss_ind, sizeof(sc->hn_rss.rss_ind));
340915516c77SSepherosa Ziehau 	if (error)
341015516c77SSepherosa Ziehau 		goto back;
341115516c77SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_HAS_RSSIND;
341215516c77SSepherosa Ziehau 
3413afd4971bSSepherosa Ziehau 	hn_rss_ind_fixup(sc);
341415516c77SSepherosa Ziehau 	error = hn_rss_reconfig(sc);
341515516c77SSepherosa Ziehau back:
341615516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
341715516c77SSepherosa Ziehau 	return (error);
341815516c77SSepherosa Ziehau }
341915516c77SSepherosa Ziehau 
342034d68912SSepherosa Ziehau #endif	/* !RSS */
342134d68912SSepherosa Ziehau 
342215516c77SSepherosa Ziehau static int
342315516c77SSepherosa Ziehau hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS)
342415516c77SSepherosa Ziehau {
342515516c77SSepherosa Ziehau 	struct hn_softc *sc = arg1;
342615516c77SSepherosa Ziehau 	char hash_str[128];
342715516c77SSepherosa Ziehau 	uint32_t hash;
342815516c77SSepherosa Ziehau 
342915516c77SSepherosa Ziehau 	HN_LOCK(sc);
343015516c77SSepherosa Ziehau 	hash = sc->hn_rss_hash;
343115516c77SSepherosa Ziehau 	HN_UNLOCK(sc);
343215516c77SSepherosa Ziehau 	snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS);
343315516c77SSepherosa Ziehau 	return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req);
343415516c77SSepherosa Ziehau }
343515516c77SSepherosa Ziehau 
343615516c77SSepherosa Ziehau static int
343740d60d6eSDexuan Cui hn_vf_sysctl(SYSCTL_HANDLER_ARGS)
343840d60d6eSDexuan Cui {
343940d60d6eSDexuan Cui 	struct hn_softc *sc = arg1;
3440499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
344140d60d6eSDexuan Cui 	struct ifnet *vf;
344240d60d6eSDexuan Cui 
344340d60d6eSDexuan Cui 	HN_LOCK(sc);
344440d60d6eSDexuan Cui 	vf_name[0] = '\0';
3445499c3e17SSepherosa Ziehau 	vf = sc->hn_vf_ifp;
344640d60d6eSDexuan Cui 	if (vf != NULL)
344740d60d6eSDexuan Cui 		snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf));
344840d60d6eSDexuan Cui 	HN_UNLOCK(sc);
344940d60d6eSDexuan Cui 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
345040d60d6eSDexuan Cui }
345140d60d6eSDexuan Cui 
345240d60d6eSDexuan Cui static int
3453499c3e17SSepherosa Ziehau hn_rxvf_sysctl(SYSCTL_HANDLER_ARGS)
3454499c3e17SSepherosa Ziehau {
3455499c3e17SSepherosa Ziehau 	struct hn_softc *sc = arg1;
3456499c3e17SSepherosa Ziehau 	char vf_name[IFNAMSIZ + 1];
3457499c3e17SSepherosa Ziehau 	struct ifnet *vf;
3458499c3e17SSepherosa Ziehau 
3459499c3e17SSepherosa Ziehau 	HN_LOCK(sc);
3460499c3e17SSepherosa Ziehau 	vf_name[0] = '\0';
3461499c3e17SSepherosa Ziehau 	vf = sc->hn_rx_ring[0].hn_rxvf_ifp;
3462499c3e17SSepherosa Ziehau 	if (vf != NULL)
3463499c3e17SSepherosa Ziehau 		snprintf(vf_name, sizeof(vf_name), "%s", if_name(vf));
3464499c3e17SSepherosa Ziehau 	HN_UNLOCK(sc);
3465499c3e17SSepherosa Ziehau 	return sysctl_handle_string(oidp, vf_name, sizeof(vf_name), req);
3466499c3e17SSepherosa Ziehau }
3467499c3e17SSepherosa Ziehau 
3468499c3e17SSepherosa Ziehau static int
3469499c3e17SSepherosa Ziehau hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
3470499c3e17SSepherosa Ziehau {
3471499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
3472499c3e17SSepherosa Ziehau 	struct sbuf *sb;
3473499c3e17SSepherosa Ziehau 	int error, i;
3474499c3e17SSepherosa Ziehau 	bool first;
3475499c3e17SSepherosa Ziehau 
3476499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
3477499c3e17SSepherosa Ziehau 	if (error != 0)
3478499c3e17SSepherosa Ziehau 		return (error);
3479499c3e17SSepherosa Ziehau 
3480499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
3481499c3e17SSepherosa Ziehau 	if (sb == NULL)
3482499c3e17SSepherosa Ziehau 		return (ENOMEM);
3483499c3e17SSepherosa Ziehau 
3484499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
3485499c3e17SSepherosa Ziehau 
3486499c3e17SSepherosa Ziehau 	first = true;
3487499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
3488499c3e17SSepherosa Ziehau 		struct ifnet *ifp;
3489499c3e17SSepherosa Ziehau 
3490499c3e17SSepherosa Ziehau 		if (hn_vfmap[i] == NULL)
3491499c3e17SSepherosa Ziehau 			continue;
3492499c3e17SSepherosa Ziehau 
3493499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
3494499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
3495499c3e17SSepherosa Ziehau 			if (first)
3496499c3e17SSepherosa Ziehau 				sbuf_printf(sb, "%s", ifp->if_xname);
3497499c3e17SSepherosa Ziehau 			else
3498499c3e17SSepherosa Ziehau 				sbuf_printf(sb, " %s", ifp->if_xname);
3499499c3e17SSepherosa Ziehau 			first = false;
3500499c3e17SSepherosa Ziehau 		}
3501499c3e17SSepherosa Ziehau 	}
3502499c3e17SSepherosa Ziehau 
3503499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
3504499c3e17SSepherosa Ziehau 
3505499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
3506499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
3507499c3e17SSepherosa Ziehau 	return (error);
3508499c3e17SSepherosa Ziehau }
3509499c3e17SSepherosa Ziehau 
3510499c3e17SSepherosa Ziehau static int
3511499c3e17SSepherosa Ziehau hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
3512499c3e17SSepherosa Ziehau {
3513499c3e17SSepherosa Ziehau 	struct rm_priotracker pt;
3514499c3e17SSepherosa Ziehau 	struct sbuf *sb;
3515499c3e17SSepherosa Ziehau 	int error, i;
3516499c3e17SSepherosa Ziehau 	bool first;
3517499c3e17SSepherosa Ziehau 
3518499c3e17SSepherosa Ziehau 	error = sysctl_wire_old_buffer(req, 0);
3519499c3e17SSepherosa Ziehau 	if (error != 0)
3520499c3e17SSepherosa Ziehau 		return (error);
3521499c3e17SSepherosa Ziehau 
3522499c3e17SSepherosa Ziehau 	sb = sbuf_new_for_sysctl(NULL, NULL, 128, req);
3523499c3e17SSepherosa Ziehau 	if (sb == NULL)
3524499c3e17SSepherosa Ziehau 		return (ENOMEM);
3525499c3e17SSepherosa Ziehau 
3526499c3e17SSepherosa Ziehau 	rm_rlock(&hn_vfmap_lock, &pt);
3527499c3e17SSepherosa Ziehau 
3528499c3e17SSepherosa Ziehau 	first = true;
3529499c3e17SSepherosa Ziehau 	for (i = 0; i < hn_vfmap_size; ++i) {
3530499c3e17SSepherosa Ziehau 		struct ifnet *ifp, *hn_ifp;
3531499c3e17SSepherosa Ziehau 
3532499c3e17SSepherosa Ziehau 		hn_ifp = hn_vfmap[i];
3533499c3e17SSepherosa Ziehau 		if (hn_ifp == NULL)
3534499c3e17SSepherosa Ziehau 			continue;
3535499c3e17SSepherosa Ziehau 
3536499c3e17SSepherosa Ziehau 		ifp = ifnet_byindex(i);
3537499c3e17SSepherosa Ziehau 		if (ifp != NULL) {
3538499c3e17SSepherosa Ziehau 			if (first) {
3539499c3e17SSepherosa Ziehau 				sbuf_printf(sb, "%s:%s", ifp->if_xname,
3540499c3e17SSepherosa Ziehau 				    hn_ifp->if_xname);
3541499c3e17SSepherosa Ziehau 			} else {
3542499c3e17SSepherosa Ziehau 				sbuf_printf(sb, " %s:%s", ifp->if_xname,
3543499c3e17SSepherosa Ziehau 				    hn_ifp->if_xname);
3544499c3e17SSepherosa Ziehau 			}
3545499c3e17SSepherosa Ziehau 			first = false;
3546499c3e17SSepherosa Ziehau 		}
3547499c3e17SSepherosa Ziehau 	}
3548499c3e17SSepherosa Ziehau 
3549499c3e17SSepherosa Ziehau 	rm_runlock(&hn_vfmap_lock, &pt);
3550499c3e17SSepherosa Ziehau 
3551499c3e17SSepherosa Ziehau 	error = sbuf_finish(sb);
3552499c3e17SSepherosa Ziehau 	sbuf_delete(sb);
3553499c3e17SSepherosa Ziehau 	return (error);
3554499c3e17SSepherosa Ziehau }
3555499c3e17SSepherosa Ziehau 
3556499c3e17SSepherosa Ziehau static int
355715516c77SSepherosa Ziehau hn_check_iplen(const struct mbuf *m, int hoff)
355815516c77SSepherosa Ziehau {
355915516c77SSepherosa Ziehau 	const struct ip *ip;
356015516c77SSepherosa Ziehau 	int len, iphlen, iplen;
356115516c77SSepherosa Ziehau 	const struct tcphdr *th;
356215516c77SSepherosa Ziehau 	int thoff;				/* TCP data offset */
356315516c77SSepherosa Ziehau 
356415516c77SSepherosa Ziehau 	len = hoff + sizeof(struct ip);
356515516c77SSepherosa Ziehau 
356615516c77SSepherosa Ziehau 	/* The packet must be at least the size of an IP header. */
356715516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < len)
356815516c77SSepherosa Ziehau 		return IPPROTO_DONE;
356915516c77SSepherosa Ziehau 
357015516c77SSepherosa Ziehau 	/* The fixed IP header must reside completely in the first mbuf. */
357115516c77SSepherosa Ziehau 	if (m->m_len < len)
357215516c77SSepherosa Ziehau 		return IPPROTO_DONE;
357315516c77SSepherosa Ziehau 
357415516c77SSepherosa Ziehau 	ip = mtodo(m, hoff);
357515516c77SSepherosa Ziehau 
357615516c77SSepherosa Ziehau 	/* Bound check the packet's stated IP header length. */
357715516c77SSepherosa Ziehau 	iphlen = ip->ip_hl << 2;
357815516c77SSepherosa Ziehau 	if (iphlen < sizeof(struct ip))		/* minimum header length */
357915516c77SSepherosa Ziehau 		return IPPROTO_DONE;
358015516c77SSepherosa Ziehau 
358115516c77SSepherosa Ziehau 	/* The full IP header must reside completely in the one mbuf. */
358215516c77SSepherosa Ziehau 	if (m->m_len < hoff + iphlen)
358315516c77SSepherosa Ziehau 		return IPPROTO_DONE;
358415516c77SSepherosa Ziehau 
358515516c77SSepherosa Ziehau 	iplen = ntohs(ip->ip_len);
358615516c77SSepherosa Ziehau 
358715516c77SSepherosa Ziehau 	/*
358815516c77SSepherosa Ziehau 	 * Check that the amount of data in the buffers is as
358915516c77SSepherosa Ziehau 	 * at least much as the IP header would have us expect.
359015516c77SSepherosa Ziehau 	 */
359115516c77SSepherosa Ziehau 	if (m->m_pkthdr.len < hoff + iplen)
359215516c77SSepherosa Ziehau 		return IPPROTO_DONE;
359315516c77SSepherosa Ziehau 
359415516c77SSepherosa Ziehau 	/*
359515516c77SSepherosa Ziehau 	 * Ignore IP fragments.
359615516c77SSepherosa Ziehau 	 */
359715516c77SSepherosa Ziehau 	if (ntohs(ip->ip_off) & (IP_OFFMASK | IP_MF))
359815516c77SSepherosa Ziehau 		return IPPROTO_DONE;
359915516c77SSepherosa Ziehau 
360015516c77SSepherosa Ziehau 	/*
360115516c77SSepherosa Ziehau 	 * The TCP/IP or UDP/IP header must be entirely contained within
360215516c77SSepherosa Ziehau 	 * the first fragment of a packet.
360315516c77SSepherosa Ziehau 	 */
360415516c77SSepherosa Ziehau 	switch (ip->ip_p) {
360515516c77SSepherosa Ziehau 	case IPPROTO_TCP:
360615516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct tcphdr))
360715516c77SSepherosa Ziehau 			return IPPROTO_DONE;
360815516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct tcphdr))
360915516c77SSepherosa Ziehau 			return IPPROTO_DONE;
361015516c77SSepherosa Ziehau 		th = (const struct tcphdr *)((const uint8_t *)ip + iphlen);
361115516c77SSepherosa Ziehau 		thoff = th->th_off << 2;
361215516c77SSepherosa Ziehau 		if (thoff < sizeof(struct tcphdr) || thoff + iphlen > iplen)
361315516c77SSepherosa Ziehau 			return IPPROTO_DONE;
361415516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + thoff)
361515516c77SSepherosa Ziehau 			return IPPROTO_DONE;
361615516c77SSepherosa Ziehau 		break;
361715516c77SSepherosa Ziehau 	case IPPROTO_UDP:
361815516c77SSepherosa Ziehau 		if (iplen < iphlen + sizeof(struct udphdr))
361915516c77SSepherosa Ziehau 			return IPPROTO_DONE;
362015516c77SSepherosa Ziehau 		if (m->m_len < hoff + iphlen + sizeof(struct udphdr))
362115516c77SSepherosa Ziehau 			return IPPROTO_DONE;
362215516c77SSepherosa Ziehau 		break;
362315516c77SSepherosa Ziehau 	default:
362415516c77SSepherosa Ziehau 		if (iplen < iphlen)
362515516c77SSepherosa Ziehau 			return IPPROTO_DONE;
362615516c77SSepherosa Ziehau 		break;
362715516c77SSepherosa Ziehau 	}
362815516c77SSepherosa Ziehau 	return ip->ip_p;
362915516c77SSepherosa Ziehau }
363015516c77SSepherosa Ziehau 
363115516c77SSepherosa Ziehau static int
363215516c77SSepherosa Ziehau hn_create_rx_data(struct hn_softc *sc, int ring_cnt)
363315516c77SSepherosa Ziehau {
363415516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
363515516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
363615516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
363715516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
363815516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
363915516c77SSepherosa Ziehau 	int lroent_cnt;
364015516c77SSepherosa Ziehau #endif
364115516c77SSepherosa Ziehau #endif
364215516c77SSepherosa Ziehau 	int i;
364315516c77SSepherosa Ziehau 
364415516c77SSepherosa Ziehau 	/*
364515516c77SSepherosa Ziehau 	 * Create RXBUF for reception.
364615516c77SSepherosa Ziehau 	 *
364715516c77SSepherosa Ziehau 	 * NOTE:
364815516c77SSepherosa Ziehau 	 * - It is shared by all channels.
364915516c77SSepherosa Ziehau 	 * - A large enough buffer is allocated, certain version of NVSes
365015516c77SSepherosa Ziehau 	 *   may further limit the usable space.
365115516c77SSepherosa Ziehau 	 */
365215516c77SSepherosa Ziehau 	sc->hn_rxbuf = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
365315516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_RXBUF_SIZE, &sc->hn_rxbuf_dma,
365415516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
365515516c77SSepherosa Ziehau 	if (sc->hn_rxbuf == NULL) {
365615516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate rxbuf failed\n");
365715516c77SSepherosa Ziehau 		return (ENOMEM);
365815516c77SSepherosa Ziehau 	}
365915516c77SSepherosa Ziehau 
366015516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = ring_cnt;
366115516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = sc->hn_rx_ring_cnt;
366215516c77SSepherosa Ziehau 
366315516c77SSepherosa Ziehau 	sc->hn_rx_ring = malloc(sizeof(struct hn_rx_ring) * sc->hn_rx_ring_cnt,
366415516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
366515516c77SSepherosa Ziehau 
366615516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
366715516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
366815516c77SSepherosa Ziehau 	lroent_cnt = hn_lro_entry_count;
366915516c77SSepherosa Ziehau 	if (lroent_cnt < TCP_LRO_ENTRIES)
367015516c77SSepherosa Ziehau 		lroent_cnt = TCP_LRO_ENTRIES;
367115516c77SSepherosa Ziehau 	if (bootverbose)
367215516c77SSepherosa Ziehau 		device_printf(dev, "LRO: entry count %d\n", lroent_cnt);
367315516c77SSepherosa Ziehau #endif
367415516c77SSepherosa Ziehau #endif	/* INET || INET6 */
367515516c77SSepherosa Ziehau 
367615516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(dev);
367715516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
367815516c77SSepherosa Ziehau 
367915516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.rx sysctl tree */
368015516c77SSepherosa Ziehau 	sc->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rx",
368115516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
368215516c77SSepherosa Ziehau 
368315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
368415516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
368515516c77SSepherosa Ziehau 
368615516c77SSepherosa Ziehau 		rxr->hn_br = hyperv_dmamem_alloc(bus_get_dma_tag(dev),
368715516c77SSepherosa Ziehau 		    PAGE_SIZE, 0, HN_TXBR_SIZE + HN_RXBR_SIZE,
368815516c77SSepherosa Ziehau 		    &rxr->hn_br_dma, BUS_DMA_WAITOK);
368915516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL) {
369015516c77SSepherosa Ziehau 			device_printf(dev, "allocate bufring failed\n");
369115516c77SSepherosa Ziehau 			return (ENOMEM);
369215516c77SSepherosa Ziehau 		}
369315516c77SSepherosa Ziehau 
369415516c77SSepherosa Ziehau 		if (hn_trust_hosttcp)
369515516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_TCP;
369615516c77SSepherosa Ziehau 		if (hn_trust_hostudp)
369715516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_UDP;
369815516c77SSepherosa Ziehau 		if (hn_trust_hostip)
369915516c77SSepherosa Ziehau 			rxr->hn_trust_hcsum |= HN_TRUST_HCSUM_IP;
370015516c77SSepherosa Ziehau 		rxr->hn_ifp = sc->hn_ifp;
370115516c77SSepherosa Ziehau 		if (i < sc->hn_tx_ring_cnt)
370215516c77SSepherosa Ziehau 			rxr->hn_txr = &sc->hn_tx_ring[i];
370315516c77SSepherosa Ziehau 		rxr->hn_pktbuf_len = HN_PKTBUF_LEN_DEF;
370415516c77SSepherosa Ziehau 		rxr->hn_pktbuf = malloc(rxr->hn_pktbuf_len, M_DEVBUF, M_WAITOK);
370515516c77SSepherosa Ziehau 		rxr->hn_rx_idx = i;
370615516c77SSepherosa Ziehau 		rxr->hn_rxbuf = sc->hn_rxbuf;
370715516c77SSepherosa Ziehau 
370815516c77SSepherosa Ziehau 		/*
370915516c77SSepherosa Ziehau 		 * Initialize LRO.
371015516c77SSepherosa Ziehau 		 */
371115516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
371215516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100095
371315516c77SSepherosa Ziehau 		tcp_lro_init_args(&rxr->hn_lro, sc->hn_ifp, lroent_cnt,
371415516c77SSepherosa Ziehau 		    hn_lro_mbufq_depth);
371515516c77SSepherosa Ziehau #else
371615516c77SSepherosa Ziehau 		tcp_lro_init(&rxr->hn_lro);
371715516c77SSepherosa Ziehau 		rxr->hn_lro.ifp = sc->hn_ifp;
371815516c77SSepherosa Ziehau #endif
371915516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
372015516c77SSepherosa Ziehau 		rxr->hn_lro.lro_length_lim = HN_LRO_LENLIM_DEF;
372115516c77SSepherosa Ziehau 		rxr->hn_lro.lro_ackcnt_lim = HN_LRO_ACKCNT_DEF;
372215516c77SSepherosa Ziehau #endif
372315516c77SSepherosa Ziehau #endif	/* INET || INET6 */
372415516c77SSepherosa Ziehau 
372515516c77SSepherosa Ziehau 		if (sc->hn_rx_sysctl_tree != NULL) {
372615516c77SSepherosa Ziehau 			char name[16];
372715516c77SSepherosa Ziehau 
372815516c77SSepherosa Ziehau 			/*
372915516c77SSepherosa Ziehau 			 * Create per RX ring sysctl tree:
373015516c77SSepherosa Ziehau 			 * dev.hn.UNIT.rx.RINGID
373115516c77SSepherosa Ziehau 			 */
373215516c77SSepherosa Ziehau 			snprintf(name, sizeof(name), "%d", i);
373315516c77SSepherosa Ziehau 			rxr->hn_rx_sysctl_tree = SYSCTL_ADD_NODE(ctx,
373415516c77SSepherosa Ziehau 			    SYSCTL_CHILDREN(sc->hn_rx_sysctl_tree),
373515516c77SSepherosa Ziehau 			    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
373615516c77SSepherosa Ziehau 
373715516c77SSepherosa Ziehau 			if (rxr->hn_rx_sysctl_tree != NULL) {
373815516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
373915516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
374015516c77SSepherosa Ziehau 				    OID_AUTO, "packets", CTLFLAG_RW,
374115516c77SSepherosa Ziehau 				    &rxr->hn_pkts, "# of packets received");
374215516c77SSepherosa Ziehau 				SYSCTL_ADD_ULONG(ctx,
374315516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
374415516c77SSepherosa Ziehau 				    OID_AUTO, "rss_pkts", CTLFLAG_RW,
374515516c77SSepherosa Ziehau 				    &rxr->hn_rss_pkts,
374615516c77SSepherosa Ziehau 				    "# of packets w/ RSS info received");
374715516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx,
374815516c77SSepherosa Ziehau 				    SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree),
374915516c77SSepherosa Ziehau 				    OID_AUTO, "pktbuf_len", CTLFLAG_RD,
375015516c77SSepherosa Ziehau 				    &rxr->hn_pktbuf_len, 0,
375115516c77SSepherosa Ziehau 				    "Temporary channel packet buffer length");
375215516c77SSepherosa Ziehau 			}
375315516c77SSepherosa Ziehau 		}
375415516c77SSepherosa Ziehau 	}
375515516c77SSepherosa Ziehau 
375615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_queued",
375715516c77SSepherosa Ziehau 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
375815516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_queued),
375915516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
376015516c77SSepherosa Ziehau 	    hn_rx_stat_int_sysctl,
376115516c77SSepherosa Ziehau #else
376215516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
376315516c77SSepherosa Ziehau #endif
376415516c77SSepherosa Ziehau 	    "LU", "LRO queued");
376515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_flushed",
376615516c77SSepherosa Ziehau 	    CTLTYPE_U64 | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
376715516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro.lro_flushed),
376815516c77SSepherosa Ziehau #if __FreeBSD_version < 1100095
376915516c77SSepherosa Ziehau 	    hn_rx_stat_int_sysctl,
377015516c77SSepherosa Ziehau #else
377115516c77SSepherosa Ziehau 	    hn_rx_stat_u64_sysctl,
377215516c77SSepherosa Ziehau #endif
377315516c77SSepherosa Ziehau 	    "LU", "LRO flushed");
377415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_tried",
377515516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
377615516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_lro_tried),
377715516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of LRO tries");
377815516c77SSepherosa Ziehau #if __FreeBSD_version >= 1100099
377915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_length_lim",
378015516c77SSepherosa Ziehau 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
378115516c77SSepherosa Ziehau 	    hn_lro_lenlim_sysctl, "IU",
378215516c77SSepherosa Ziehau 	    "Max # of data bytes to be aggregated by LRO");
378315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "lro_ackcnt_lim",
378415516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
378515516c77SSepherosa Ziehau 	    hn_lro_ackcnt_sysctl, "I",
378615516c77SSepherosa Ziehau 	    "Max # of ACKs to be aggregated by LRO");
378715516c77SSepherosa Ziehau #endif
378815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hosttcp",
378915516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_TCP,
379015516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
379115516c77SSepherosa Ziehau 	    "Trust tcp segement verification on host side, "
379215516c77SSepherosa Ziehau 	    "when csum info is missing");
379315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostudp",
379415516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_UDP,
379515516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
379615516c77SSepherosa Ziehau 	    "Trust udp datagram verification on host side, "
379715516c77SSepherosa Ziehau 	    "when csum info is missing");
379815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "trust_hostip",
379915516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, HN_TRUST_HCSUM_IP,
380015516c77SSepherosa Ziehau 	    hn_trust_hcsum_sysctl, "I",
380115516c77SSepherosa Ziehau 	    "Trust ip packet verification on host side, "
380215516c77SSepherosa Ziehau 	    "when csum info is missing");
380315516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_ip",
380415516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
380515516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_ip),
380615516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM IP");
380715516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_tcp",
380815516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
380915516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_tcp),
381015516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM TCP");
381115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_udp",
381215516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
381315516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_udp),
381415516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "RXCSUM UDP");
381515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "csum_trusted",
381615516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
381715516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_csum_trusted),
381815516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU",
381915516c77SSepherosa Ziehau 	    "# of packets that we trust host's csum verification");
382015516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "small_pkts",
382115516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
382215516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_small_pkts),
382315516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of small packets received");
382415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_ack_failed",
382515516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
382615516c77SSepherosa Ziehau 	    __offsetof(struct hn_rx_ring, hn_ack_failed),
382715516c77SSepherosa Ziehau 	    hn_rx_stat_ulong_sysctl, "LU", "# of RXBUF ack failures");
382815516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_cnt",
382915516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_cnt, 0, "# created RX rings");
383015516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_inuse",
383115516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_rx_ring_inuse, 0, "# used RX rings");
383215516c77SSepherosa Ziehau 
383315516c77SSepherosa Ziehau 	return (0);
383415516c77SSepherosa Ziehau }
383515516c77SSepherosa Ziehau 
383615516c77SSepherosa Ziehau static void
383715516c77SSepherosa Ziehau hn_destroy_rx_data(struct hn_softc *sc)
383815516c77SSepherosa Ziehau {
383915516c77SSepherosa Ziehau 	int i;
384015516c77SSepherosa Ziehau 
384115516c77SSepherosa Ziehau 	if (sc->hn_rxbuf != NULL) {
38422494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_RXBUF_REF) == 0)
384315516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_rxbuf_dma, sc->hn_rxbuf);
38442494d735SSepherosa Ziehau 		else
38452494d735SSepherosa Ziehau 			device_printf(sc->hn_dev, "RXBUF is referenced\n");
384615516c77SSepherosa Ziehau 		sc->hn_rxbuf = NULL;
384715516c77SSepherosa Ziehau 	}
384815516c77SSepherosa Ziehau 
384915516c77SSepherosa Ziehau 	if (sc->hn_rx_ring_cnt == 0)
385015516c77SSepherosa Ziehau 		return;
385115516c77SSepherosa Ziehau 
385215516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
385315516c77SSepherosa Ziehau 		struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
385415516c77SSepherosa Ziehau 
385515516c77SSepherosa Ziehau 		if (rxr->hn_br == NULL)
385615516c77SSepherosa Ziehau 			continue;
38572494d735SSepherosa Ziehau 		if ((rxr->hn_rx_flags & HN_RX_FLAG_BR_REF) == 0) {
385815516c77SSepherosa Ziehau 			hyperv_dmamem_free(&rxr->hn_br_dma, rxr->hn_br);
38592494d735SSepherosa Ziehau 		} else {
38602494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
38612494d735SSepherosa Ziehau 			    "%dth channel bufring is referenced", i);
38622494d735SSepherosa Ziehau 		}
386315516c77SSepherosa Ziehau 		rxr->hn_br = NULL;
386415516c77SSepherosa Ziehau 
386515516c77SSepherosa Ziehau #if defined(INET) || defined(INET6)
386615516c77SSepherosa Ziehau 		tcp_lro_free(&rxr->hn_lro);
386715516c77SSepherosa Ziehau #endif
386815516c77SSepherosa Ziehau 		free(rxr->hn_pktbuf, M_DEVBUF);
386915516c77SSepherosa Ziehau 	}
387015516c77SSepherosa Ziehau 	free(sc->hn_rx_ring, M_DEVBUF);
387115516c77SSepherosa Ziehau 	sc->hn_rx_ring = NULL;
387215516c77SSepherosa Ziehau 
387315516c77SSepherosa Ziehau 	sc->hn_rx_ring_cnt = 0;
387415516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = 0;
387515516c77SSepherosa Ziehau }
387615516c77SSepherosa Ziehau 
387715516c77SSepherosa Ziehau static int
387815516c77SSepherosa Ziehau hn_tx_ring_create(struct hn_softc *sc, int id)
387915516c77SSepherosa Ziehau {
388015516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[id];
388115516c77SSepherosa Ziehau 	device_t dev = sc->hn_dev;
388215516c77SSepherosa Ziehau 	bus_dma_tag_t parent_dtag;
388315516c77SSepherosa Ziehau 	int error, i;
388415516c77SSepherosa Ziehau 
388515516c77SSepherosa Ziehau 	txr->hn_sc = sc;
388615516c77SSepherosa Ziehau 	txr->hn_tx_idx = id;
388715516c77SSepherosa Ziehau 
388815516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
388915516c77SSepherosa Ziehau 	mtx_init(&txr->hn_txlist_spin, "hn txlist", NULL, MTX_SPIN);
389015516c77SSepherosa Ziehau #endif
389115516c77SSepherosa Ziehau 	mtx_init(&txr->hn_tx_lock, "hn tx", NULL, MTX_DEF);
389215516c77SSepherosa Ziehau 
389315516c77SSepherosa Ziehau 	txr->hn_txdesc_cnt = HN_TX_DESC_CNT;
389415516c77SSepherosa Ziehau 	txr->hn_txdesc = malloc(sizeof(struct hn_txdesc) * txr->hn_txdesc_cnt,
389515516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
389615516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
389715516c77SSepherosa Ziehau 	SLIST_INIT(&txr->hn_txlist);
389815516c77SSepherosa Ziehau #else
389915516c77SSepherosa Ziehau 	txr->hn_txdesc_br = buf_ring_alloc(txr->hn_txdesc_cnt, M_DEVBUF,
390015516c77SSepherosa Ziehau 	    M_WAITOK, &txr->hn_tx_lock);
390115516c77SSepherosa Ziehau #endif
390215516c77SSepherosa Ziehau 
39030e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode == HN_TX_TASKQ_M_EVTTQ) {
39040e11868dSSepherosa Ziehau 		txr->hn_tx_taskq = VMBUS_GET_EVENT_TASKQ(
39050e11868dSSepherosa Ziehau 		    device_get_parent(dev), dev, HN_RING_IDX2CPU(sc, id));
39060e11868dSSepherosa Ziehau 	} else {
3907fdd0222aSSepherosa Ziehau 		txr->hn_tx_taskq = sc->hn_tx_taskqs[id % hn_tx_taskq_cnt];
39080e11868dSSepherosa Ziehau 	}
390915516c77SSepherosa Ziehau 
391023bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
391115516c77SSepherosa Ziehau 	if (hn_use_if_start) {
391215516c77SSepherosa Ziehau 		txr->hn_txeof = hn_start_txeof;
391315516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_start_taskfunc, txr);
391415516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_start_txeof_taskfunc, txr);
391523bf9e15SSepherosa Ziehau 	} else
391623bf9e15SSepherosa Ziehau #endif
391723bf9e15SSepherosa Ziehau 	{
391815516c77SSepherosa Ziehau 		int br_depth;
391915516c77SSepherosa Ziehau 
392015516c77SSepherosa Ziehau 		txr->hn_txeof = hn_xmit_txeof;
392115516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_tx_task, 0, hn_xmit_taskfunc, txr);
392215516c77SSepherosa Ziehau 		TASK_INIT(&txr->hn_txeof_task, 0, hn_xmit_txeof_taskfunc, txr);
392315516c77SSepherosa Ziehau 
392415516c77SSepherosa Ziehau 		br_depth = hn_get_txswq_depth(txr);
392515516c77SSepherosa Ziehau 		txr->hn_mbuf_br = buf_ring_alloc(br_depth, M_DEVBUF,
392615516c77SSepherosa Ziehau 		    M_WAITOK, &txr->hn_tx_lock);
392715516c77SSepherosa Ziehau 	}
392815516c77SSepherosa Ziehau 
392915516c77SSepherosa Ziehau 	txr->hn_direct_tx_size = hn_direct_tx_size;
393015516c77SSepherosa Ziehau 
393115516c77SSepherosa Ziehau 	/*
393215516c77SSepherosa Ziehau 	 * Always schedule transmission instead of trying to do direct
393315516c77SSepherosa Ziehau 	 * transmission.  This one gives the best performance so far.
393415516c77SSepherosa Ziehau 	 */
393515516c77SSepherosa Ziehau 	txr->hn_sched_tx = 1;
393615516c77SSepherosa Ziehau 
393715516c77SSepherosa Ziehau 	parent_dtag = bus_get_dma_tag(dev);
393815516c77SSepherosa Ziehau 
393915516c77SSepherosa Ziehau 	/* DMA tag for RNDIS packet messages. */
394015516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
394115516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_ALIGN,		/* alignment */
394215516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_BOUNDARY,	/* boundary */
394315516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
394415516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
394515516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
394615516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsize */
394715516c77SSepherosa Ziehau 	    1,				/* nsegments */
394815516c77SSepherosa Ziehau 	    HN_RNDIS_PKT_LEN,		/* maxsegsize */
394915516c77SSepherosa Ziehau 	    0,				/* flags */
395015516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
395115516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
395215516c77SSepherosa Ziehau 	    &txr->hn_tx_rndis_dtag);
395315516c77SSepherosa Ziehau 	if (error) {
395415516c77SSepherosa Ziehau 		device_printf(dev, "failed to create rndis dmatag\n");
395515516c77SSepherosa Ziehau 		return error;
395615516c77SSepherosa Ziehau 	}
395715516c77SSepherosa Ziehau 
395815516c77SSepherosa Ziehau 	/* DMA tag for data. */
395915516c77SSepherosa Ziehau 	error = bus_dma_tag_create(parent_dtag, /* parent */
396015516c77SSepherosa Ziehau 	    1,				/* alignment */
396115516c77SSepherosa Ziehau 	    HN_TX_DATA_BOUNDARY,	/* boundary */
396215516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* lowaddr */
396315516c77SSepherosa Ziehau 	    BUS_SPACE_MAXADDR,		/* highaddr */
396415516c77SSepherosa Ziehau 	    NULL, NULL,			/* filter, filterarg */
396515516c77SSepherosa Ziehau 	    HN_TX_DATA_MAXSIZE,		/* maxsize */
396615516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGCNT_MAX,	/* nsegments */
396715516c77SSepherosa Ziehau 	    HN_TX_DATA_SEGSIZE,		/* maxsegsize */
396815516c77SSepherosa Ziehau 	    0,				/* flags */
396915516c77SSepherosa Ziehau 	    NULL,			/* lockfunc */
397015516c77SSepherosa Ziehau 	    NULL,			/* lockfuncarg */
397115516c77SSepherosa Ziehau 	    &txr->hn_tx_data_dtag);
397215516c77SSepherosa Ziehau 	if (error) {
397315516c77SSepherosa Ziehau 		device_printf(dev, "failed to create data dmatag\n");
397415516c77SSepherosa Ziehau 		return error;
397515516c77SSepherosa Ziehau 	}
397615516c77SSepherosa Ziehau 
397715516c77SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i) {
397815516c77SSepherosa Ziehau 		struct hn_txdesc *txd = &txr->hn_txdesc[i];
397915516c77SSepherosa Ziehau 
398015516c77SSepherosa Ziehau 		txd->txr = txr;
398115516c77SSepherosa Ziehau 		txd->chim_index = HN_NVS_CHIM_IDX_INVALID;
3982dc13fee6SSepherosa Ziehau 		STAILQ_INIT(&txd->agg_list);
398315516c77SSepherosa Ziehau 
398415516c77SSepherosa Ziehau 		/*
398515516c77SSepherosa Ziehau 		 * Allocate and load RNDIS packet message.
398615516c77SSepherosa Ziehau 		 */
398715516c77SSepherosa Ziehau         	error = bus_dmamem_alloc(txr->hn_tx_rndis_dtag,
398815516c77SSepherosa Ziehau 		    (void **)&txd->rndis_pkt,
398915516c77SSepherosa Ziehau 		    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
399015516c77SSepherosa Ziehau 		    &txd->rndis_pkt_dmap);
399115516c77SSepherosa Ziehau 		if (error) {
399215516c77SSepherosa Ziehau 			device_printf(dev,
399315516c77SSepherosa Ziehau 			    "failed to allocate rndis_packet_msg, %d\n", i);
399415516c77SSepherosa Ziehau 			return error;
399515516c77SSepherosa Ziehau 		}
399615516c77SSepherosa Ziehau 
399715516c77SSepherosa Ziehau 		error = bus_dmamap_load(txr->hn_tx_rndis_dtag,
399815516c77SSepherosa Ziehau 		    txd->rndis_pkt_dmap,
399915516c77SSepherosa Ziehau 		    txd->rndis_pkt, HN_RNDIS_PKT_LEN,
400015516c77SSepherosa Ziehau 		    hyperv_dma_map_paddr, &txd->rndis_pkt_paddr,
400115516c77SSepherosa Ziehau 		    BUS_DMA_NOWAIT);
400215516c77SSepherosa Ziehau 		if (error) {
400315516c77SSepherosa Ziehau 			device_printf(dev,
400415516c77SSepherosa Ziehau 			    "failed to load rndis_packet_msg, %d\n", i);
400515516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
400615516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
400715516c77SSepherosa Ziehau 			return error;
400815516c77SSepherosa Ziehau 		}
400915516c77SSepherosa Ziehau 
401015516c77SSepherosa Ziehau 		/* DMA map for TX data. */
401115516c77SSepherosa Ziehau 		error = bus_dmamap_create(txr->hn_tx_data_dtag, 0,
401215516c77SSepherosa Ziehau 		    &txd->data_dmap);
401315516c77SSepherosa Ziehau 		if (error) {
401415516c77SSepherosa Ziehau 			device_printf(dev,
401515516c77SSepherosa Ziehau 			    "failed to allocate tx data dmamap\n");
401615516c77SSepherosa Ziehau 			bus_dmamap_unload(txr->hn_tx_rndis_dtag,
401715516c77SSepherosa Ziehau 			    txd->rndis_pkt_dmap);
401815516c77SSepherosa Ziehau 			bus_dmamem_free(txr->hn_tx_rndis_dtag,
401915516c77SSepherosa Ziehau 			    txd->rndis_pkt, txd->rndis_pkt_dmap);
402015516c77SSepherosa Ziehau 			return error;
402115516c77SSepherosa Ziehau 		}
402215516c77SSepherosa Ziehau 
402315516c77SSepherosa Ziehau 		/* All set, put it to list */
402415516c77SSepherosa Ziehau 		txd->flags |= HN_TXD_FLAG_ONLIST;
402515516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
402615516c77SSepherosa Ziehau 		SLIST_INSERT_HEAD(&txr->hn_txlist, txd, link);
402715516c77SSepherosa Ziehau #else
402815516c77SSepherosa Ziehau 		buf_ring_enqueue(txr->hn_txdesc_br, txd);
402915516c77SSepherosa Ziehau #endif
403015516c77SSepherosa Ziehau 	}
403115516c77SSepherosa Ziehau 	txr->hn_txdesc_avail = txr->hn_txdesc_cnt;
403215516c77SSepherosa Ziehau 
403315516c77SSepherosa Ziehau 	if (sc->hn_tx_sysctl_tree != NULL) {
403415516c77SSepherosa Ziehau 		struct sysctl_oid_list *child;
403515516c77SSepherosa Ziehau 		struct sysctl_ctx_list *ctx;
403615516c77SSepherosa Ziehau 		char name[16];
403715516c77SSepherosa Ziehau 
403815516c77SSepherosa Ziehau 		/*
403915516c77SSepherosa Ziehau 		 * Create per TX ring sysctl tree:
404015516c77SSepherosa Ziehau 		 * dev.hn.UNIT.tx.RINGID
404115516c77SSepherosa Ziehau 		 */
404215516c77SSepherosa Ziehau 		ctx = device_get_sysctl_ctx(dev);
404315516c77SSepherosa Ziehau 		child = SYSCTL_CHILDREN(sc->hn_tx_sysctl_tree);
404415516c77SSepherosa Ziehau 
404515516c77SSepherosa Ziehau 		snprintf(name, sizeof(name), "%d", id);
404615516c77SSepherosa Ziehau 		txr->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
404715516c77SSepherosa Ziehau 		    name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
404815516c77SSepherosa Ziehau 
404915516c77SSepherosa Ziehau 		if (txr->hn_tx_sysctl_tree != NULL) {
405015516c77SSepherosa Ziehau 			child = SYSCTL_CHILDREN(txr->hn_tx_sysctl_tree);
405115516c77SSepherosa Ziehau 
405285e4ae1eSSepherosa Ziehau #ifdef HN_DEBUG
405315516c77SSepherosa Ziehau 			SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_avail",
405415516c77SSepherosa Ziehau 			    CTLFLAG_RD, &txr->hn_txdesc_avail, 0,
405515516c77SSepherosa Ziehau 			    "# of available TX descs");
405685e4ae1eSSepherosa Ziehau #endif
405723bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
405823bf9e15SSepherosa Ziehau 			if (!hn_use_if_start)
405923bf9e15SSepherosa Ziehau #endif
406023bf9e15SSepherosa Ziehau 			{
406115516c77SSepherosa Ziehau 				SYSCTL_ADD_INT(ctx, child, OID_AUTO, "oactive",
406215516c77SSepherosa Ziehau 				    CTLFLAG_RD, &txr->hn_oactive, 0,
406315516c77SSepherosa Ziehau 				    "over active");
406415516c77SSepherosa Ziehau 			}
406515516c77SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "packets",
406615516c77SSepherosa Ziehau 			    CTLFLAG_RW, &txr->hn_pkts,
406715516c77SSepherosa Ziehau 			    "# of packets transmitted");
4068dc13fee6SSepherosa Ziehau 			SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "sends",
4069dc13fee6SSepherosa Ziehau 			    CTLFLAG_RW, &txr->hn_sends, "# of sends");
407015516c77SSepherosa Ziehau 		}
407115516c77SSepherosa Ziehau 	}
407215516c77SSepherosa Ziehau 
407315516c77SSepherosa Ziehau 	return 0;
407415516c77SSepherosa Ziehau }
407515516c77SSepherosa Ziehau 
407615516c77SSepherosa Ziehau static void
407715516c77SSepherosa Ziehau hn_txdesc_dmamap_destroy(struct hn_txdesc *txd)
407815516c77SSepherosa Ziehau {
407915516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = txd->txr;
408015516c77SSepherosa Ziehau 
408115516c77SSepherosa Ziehau 	KASSERT(txd->m == NULL, ("still has mbuf installed"));
408215516c77SSepherosa Ziehau 	KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("still dma mapped"));
408315516c77SSepherosa Ziehau 
408415516c77SSepherosa Ziehau 	bus_dmamap_unload(txr->hn_tx_rndis_dtag, txd->rndis_pkt_dmap);
408515516c77SSepherosa Ziehau 	bus_dmamem_free(txr->hn_tx_rndis_dtag, txd->rndis_pkt,
408615516c77SSepherosa Ziehau 	    txd->rndis_pkt_dmap);
408715516c77SSepherosa Ziehau 	bus_dmamap_destroy(txr->hn_tx_data_dtag, txd->data_dmap);
408815516c77SSepherosa Ziehau }
408915516c77SSepherosa Ziehau 
409015516c77SSepherosa Ziehau static void
409125641fc7SSepherosa Ziehau hn_txdesc_gc(struct hn_tx_ring *txr, struct hn_txdesc *txd)
409225641fc7SSepherosa Ziehau {
409325641fc7SSepherosa Ziehau 
409425641fc7SSepherosa Ziehau 	KASSERT(txd->refs == 0 || txd->refs == 1,
409525641fc7SSepherosa Ziehau 	    ("invalid txd refs %d", txd->refs));
409625641fc7SSepherosa Ziehau 
409725641fc7SSepherosa Ziehau 	/* Aggregated txds will be freed by their aggregating txd. */
409825641fc7SSepherosa Ziehau 	if (txd->refs > 0 && (txd->flags & HN_TXD_FLAG_ONAGG) == 0) {
409925641fc7SSepherosa Ziehau 		int freed;
410025641fc7SSepherosa Ziehau 
410125641fc7SSepherosa Ziehau 		freed = hn_txdesc_put(txr, txd);
410225641fc7SSepherosa Ziehau 		KASSERT(freed, ("can't free txdesc"));
410325641fc7SSepherosa Ziehau 	}
410425641fc7SSepherosa Ziehau }
410525641fc7SSepherosa Ziehau 
410625641fc7SSepherosa Ziehau static void
410715516c77SSepherosa Ziehau hn_tx_ring_destroy(struct hn_tx_ring *txr)
410815516c77SSepherosa Ziehau {
410925641fc7SSepherosa Ziehau 	int i;
411015516c77SSepherosa Ziehau 
411115516c77SSepherosa Ziehau 	if (txr->hn_txdesc == NULL)
411215516c77SSepherosa Ziehau 		return;
411315516c77SSepherosa Ziehau 
411425641fc7SSepherosa Ziehau 	/*
411525641fc7SSepherosa Ziehau 	 * NOTE:
411625641fc7SSepherosa Ziehau 	 * Because the freeing of aggregated txds will be deferred
411725641fc7SSepherosa Ziehau 	 * to the aggregating txd, two passes are used here:
411825641fc7SSepherosa Ziehau 	 * - The first pass GCes any pending txds.  This GC is necessary,
411925641fc7SSepherosa Ziehau 	 *   since if the channels are revoked, hypervisor will not
412025641fc7SSepherosa Ziehau 	 *   deliver send-done for all pending txds.
412125641fc7SSepherosa Ziehau 	 * - The second pass frees the busdma stuffs, i.e. after all txds
412225641fc7SSepherosa Ziehau 	 *   were freed.
412325641fc7SSepherosa Ziehau 	 */
412425641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
412525641fc7SSepherosa Ziehau 		hn_txdesc_gc(txr, &txr->hn_txdesc[i]);
412625641fc7SSepherosa Ziehau 	for (i = 0; i < txr->hn_txdesc_cnt; ++i)
412725641fc7SSepherosa Ziehau 		hn_txdesc_dmamap_destroy(&txr->hn_txdesc[i]);
412815516c77SSepherosa Ziehau 
412915516c77SSepherosa Ziehau 	if (txr->hn_tx_data_dtag != NULL)
413015516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_data_dtag);
413115516c77SSepherosa Ziehau 	if (txr->hn_tx_rndis_dtag != NULL)
413215516c77SSepherosa Ziehau 		bus_dma_tag_destroy(txr->hn_tx_rndis_dtag);
413315516c77SSepherosa Ziehau 
413415516c77SSepherosa Ziehau #ifdef HN_USE_TXDESC_BUFRING
413515516c77SSepherosa Ziehau 	buf_ring_free(txr->hn_txdesc_br, M_DEVBUF);
413615516c77SSepherosa Ziehau #endif
413715516c77SSepherosa Ziehau 
413815516c77SSepherosa Ziehau 	free(txr->hn_txdesc, M_DEVBUF);
413915516c77SSepherosa Ziehau 	txr->hn_txdesc = NULL;
414015516c77SSepherosa Ziehau 
414115516c77SSepherosa Ziehau 	if (txr->hn_mbuf_br != NULL)
414215516c77SSepherosa Ziehau 		buf_ring_free(txr->hn_mbuf_br, M_DEVBUF);
414315516c77SSepherosa Ziehau 
414415516c77SSepherosa Ziehau #ifndef HN_USE_TXDESC_BUFRING
414515516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_txlist_spin);
414615516c77SSepherosa Ziehau #endif
414715516c77SSepherosa Ziehau 	mtx_destroy(&txr->hn_tx_lock);
414815516c77SSepherosa Ziehau }
414915516c77SSepherosa Ziehau 
415015516c77SSepherosa Ziehau static int
415115516c77SSepherosa Ziehau hn_create_tx_data(struct hn_softc *sc, int ring_cnt)
415215516c77SSepherosa Ziehau {
415315516c77SSepherosa Ziehau 	struct sysctl_oid_list *child;
415415516c77SSepherosa Ziehau 	struct sysctl_ctx_list *ctx;
415515516c77SSepherosa Ziehau 	int i;
415615516c77SSepherosa Ziehau 
415715516c77SSepherosa Ziehau 	/*
415815516c77SSepherosa Ziehau 	 * Create TXBUF for chimney sending.
415915516c77SSepherosa Ziehau 	 *
416015516c77SSepherosa Ziehau 	 * NOTE: It is shared by all channels.
416115516c77SSepherosa Ziehau 	 */
416215516c77SSepherosa Ziehau 	sc->hn_chim = hyperv_dmamem_alloc(bus_get_dma_tag(sc->hn_dev),
416315516c77SSepherosa Ziehau 	    PAGE_SIZE, 0, HN_CHIM_SIZE, &sc->hn_chim_dma,
416415516c77SSepherosa Ziehau 	    BUS_DMA_WAITOK | BUS_DMA_ZERO);
416515516c77SSepherosa Ziehau 	if (sc->hn_chim == NULL) {
416615516c77SSepherosa Ziehau 		device_printf(sc->hn_dev, "allocate txbuf failed\n");
416715516c77SSepherosa Ziehau 		return (ENOMEM);
416815516c77SSepherosa Ziehau 	}
416915516c77SSepherosa Ziehau 
417015516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = ring_cnt;
417115516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
417215516c77SSepherosa Ziehau 
417315516c77SSepherosa Ziehau 	sc->hn_tx_ring = malloc(sizeof(struct hn_tx_ring) * sc->hn_tx_ring_cnt,
417415516c77SSepherosa Ziehau 	    M_DEVBUF, M_WAITOK | M_ZERO);
417515516c77SSepherosa Ziehau 
417615516c77SSepherosa Ziehau 	ctx = device_get_sysctl_ctx(sc->hn_dev);
417715516c77SSepherosa Ziehau 	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->hn_dev));
417815516c77SSepherosa Ziehau 
417915516c77SSepherosa Ziehau 	/* Create dev.hn.UNIT.tx sysctl tree */
418015516c77SSepherosa Ziehau 	sc->hn_tx_sysctl_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tx",
418115516c77SSepherosa Ziehau 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
418215516c77SSepherosa Ziehau 
418315516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
418415516c77SSepherosa Ziehau 		int error;
418515516c77SSepherosa Ziehau 
418615516c77SSepherosa Ziehau 		error = hn_tx_ring_create(sc, i);
418715516c77SSepherosa Ziehau 		if (error)
418815516c77SSepherosa Ziehau 			return error;
418915516c77SSepherosa Ziehau 	}
419015516c77SSepherosa Ziehau 
419115516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "no_txdescs",
419215516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
419315516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_no_txdescs),
419415516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of times short of TX descs");
419515516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "send_failed",
419615516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
419715516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_send_failed),
419815516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of hyper-v sending failure");
419915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "txdma_failed",
420015516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
420115516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_txdma_failed),
420215516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX DMA failure");
4203dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_flush_failed",
4204dc13fee6SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
4205dc13fee6SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_flush_failed),
4206dc13fee6SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU",
4207dc13fee6SSepherosa Ziehau 	    "# of packet transmission aggregation flush failure");
420815516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_collapsed",
420915516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
421015516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_collapsed),
421115516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of TX mbuf collapsed");
421215516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney",
421315516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
421415516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney),
421515516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send");
421615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_tried",
421715516c77SSepherosa Ziehau 	    CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
421815516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_tx_chimney_tried),
421915516c77SSepherosa Ziehau 	    hn_tx_stat_ulong_sysctl, "LU", "# of chimney send tries");
422015516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_cnt",
422115516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_txdesc_cnt, 0,
422215516c77SSepherosa Ziehau 	    "# of total TX descs");
422315516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_chimney_max",
422415516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_chim_szmax, 0,
422515516c77SSepherosa Ziehau 	    "Chimney send packet size upper boundary");
422615516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_size",
422715516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
422815516c77SSepherosa Ziehau 	    hn_chim_size_sysctl, "I", "Chimney send packet size limit");
422915516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "direct_tx_size",
423015516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
423115516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_direct_tx_size),
423215516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
423315516c77SSepherosa Ziehau 	    "Size of the packet for direct transmission");
423415516c77SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "sched_tx",
423515516c77SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc,
423615516c77SSepherosa Ziehau 	    __offsetof(struct hn_tx_ring, hn_sched_tx),
423715516c77SSepherosa Ziehau 	    hn_tx_conf_int_sysctl, "I",
423815516c77SSepherosa Ziehau 	    "Always schedule transmission "
423915516c77SSepherosa Ziehau 	    "instead of doing direct transmission");
424015516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_cnt",
424115516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_cnt, 0, "# created TX rings");
424215516c77SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_ring_inuse",
424315516c77SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring_inuse, 0, "# used TX rings");
4244dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "agg_szmax",
4245dc13fee6SSepherosa Ziehau 	    CTLFLAG_RD, &sc->hn_tx_ring[0].hn_agg_szmax, 0,
4246dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation size");
4247dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_pktmax",
4248dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
4249dc13fee6SSepherosa Ziehau 	    hn_txagg_pktmax_sysctl, "I",
4250dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation packets");
4251dc13fee6SSepherosa Ziehau 	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "agg_align",
4252dc13fee6SSepherosa Ziehau 	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
4253dc13fee6SSepherosa Ziehau 	    hn_txagg_align_sysctl, "I",
4254dc13fee6SSepherosa Ziehau 	    "Applied packet transmission aggregation alignment");
425515516c77SSepherosa Ziehau 
425615516c77SSepherosa Ziehau 	return 0;
425715516c77SSepherosa Ziehau }
425815516c77SSepherosa Ziehau 
425915516c77SSepherosa Ziehau static void
426015516c77SSepherosa Ziehau hn_set_chim_size(struct hn_softc *sc, int chim_size)
426115516c77SSepherosa Ziehau {
426215516c77SSepherosa Ziehau 	int i;
426315516c77SSepherosa Ziehau 
4264a7ba7648SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
426515516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_chim_size = chim_size;
426615516c77SSepherosa Ziehau }
426715516c77SSepherosa Ziehau 
426815516c77SSepherosa Ziehau static void
426915516c77SSepherosa Ziehau hn_set_tso_maxsize(struct hn_softc *sc, int tso_maxlen, int mtu)
427015516c77SSepherosa Ziehau {
427115516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
427215516c77SSepherosa Ziehau 	int tso_minlen;
427315516c77SSepherosa Ziehau 
427415516c77SSepherosa Ziehau 	if ((ifp->if_capabilities & (IFCAP_TSO4 | IFCAP_TSO6)) == 0)
427515516c77SSepherosa Ziehau 		return;
427615516c77SSepherosa Ziehau 
427715516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_sgmin >= 2,
427815516c77SSepherosa Ziehau 	    ("invalid NDIS tso sgmin %d", sc->hn_ndis_tso_sgmin));
427915516c77SSepherosa Ziehau 	tso_minlen = sc->hn_ndis_tso_sgmin * mtu;
428015516c77SSepherosa Ziehau 
428115516c77SSepherosa Ziehau 	KASSERT(sc->hn_ndis_tso_szmax >= tso_minlen &&
428215516c77SSepherosa Ziehau 	    sc->hn_ndis_tso_szmax <= IP_MAXPACKET,
428315516c77SSepherosa Ziehau 	    ("invalid NDIS tso szmax %d", sc->hn_ndis_tso_szmax));
428415516c77SSepherosa Ziehau 
428515516c77SSepherosa Ziehau 	if (tso_maxlen < tso_minlen)
428615516c77SSepherosa Ziehau 		tso_maxlen = tso_minlen;
428715516c77SSepherosa Ziehau 	else if (tso_maxlen > IP_MAXPACKET)
428815516c77SSepherosa Ziehau 		tso_maxlen = IP_MAXPACKET;
428915516c77SSepherosa Ziehau 	if (tso_maxlen > sc->hn_ndis_tso_szmax)
429015516c77SSepherosa Ziehau 		tso_maxlen = sc->hn_ndis_tso_szmax;
429115516c77SSepherosa Ziehau 	ifp->if_hw_tsomax = tso_maxlen - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
429215516c77SSepherosa Ziehau 	if (bootverbose)
429315516c77SSepherosa Ziehau 		if_printf(ifp, "TSO size max %u\n", ifp->if_hw_tsomax);
429415516c77SSepherosa Ziehau }
429515516c77SSepherosa Ziehau 
429615516c77SSepherosa Ziehau static void
429715516c77SSepherosa Ziehau hn_fixup_tx_data(struct hn_softc *sc)
429815516c77SSepherosa Ziehau {
429915516c77SSepherosa Ziehau 	uint64_t csum_assist;
430015516c77SSepherosa Ziehau 	int i;
430115516c77SSepherosa Ziehau 
430215516c77SSepherosa Ziehau 	hn_set_chim_size(sc, sc->hn_chim_szmax);
430315516c77SSepherosa Ziehau 	if (hn_tx_chimney_size > 0 &&
430415516c77SSepherosa Ziehau 	    hn_tx_chimney_size < sc->hn_chim_szmax)
430515516c77SSepherosa Ziehau 		hn_set_chim_size(sc, hn_tx_chimney_size);
430615516c77SSepherosa Ziehau 
430715516c77SSepherosa Ziehau 	csum_assist = 0;
430815516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_IPCS)
430915516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP;
431015516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP4CS)
431115516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_TCP;
431215516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_UDP4CS)
431315516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP_UDP;
431415516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_TCP6CS)
431515516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_TCP;
431615516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_UDP6CS)
431715516c77SSepherosa Ziehau 		csum_assist |= CSUM_IP6_UDP;
431815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
431915516c77SSepherosa Ziehau 		sc->hn_tx_ring[i].hn_csum_assist = csum_assist;
432015516c77SSepherosa Ziehau 
432115516c77SSepherosa Ziehau 	if (sc->hn_caps & HN_CAP_HASHVAL) {
432215516c77SSepherosa Ziehau 		/*
432315516c77SSepherosa Ziehau 		 * Support HASHVAL pktinfo on TX path.
432415516c77SSepherosa Ziehau 		 */
432515516c77SSepherosa Ziehau 		if (bootverbose)
432615516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "support HASHVAL pktinfo\n");
432715516c77SSepherosa Ziehau 		for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
432815516c77SSepherosa Ziehau 			sc->hn_tx_ring[i].hn_tx_flags |= HN_TX_FLAG_HASHVAL;
432915516c77SSepherosa Ziehau 	}
433015516c77SSepherosa Ziehau }
433115516c77SSepherosa Ziehau 
433215516c77SSepherosa Ziehau static void
433315516c77SSepherosa Ziehau hn_destroy_tx_data(struct hn_softc *sc)
433415516c77SSepherosa Ziehau {
433515516c77SSepherosa Ziehau 	int i;
433615516c77SSepherosa Ziehau 
433715516c77SSepherosa Ziehau 	if (sc->hn_chim != NULL) {
43382494d735SSepherosa Ziehau 		if ((sc->hn_flags & HN_FLAG_CHIM_REF) == 0) {
433915516c77SSepherosa Ziehau 			hyperv_dmamem_free(&sc->hn_chim_dma, sc->hn_chim);
43402494d735SSepherosa Ziehau 		} else {
43412494d735SSepherosa Ziehau 			device_printf(sc->hn_dev,
43422494d735SSepherosa Ziehau 			    "chimney sending buffer is referenced");
43432494d735SSepherosa Ziehau 		}
434415516c77SSepherosa Ziehau 		sc->hn_chim = NULL;
434515516c77SSepherosa Ziehau 	}
434615516c77SSepherosa Ziehau 
434715516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt == 0)
434815516c77SSepherosa Ziehau 		return;
434915516c77SSepherosa Ziehau 
435015516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
435115516c77SSepherosa Ziehau 		hn_tx_ring_destroy(&sc->hn_tx_ring[i]);
435215516c77SSepherosa Ziehau 
435315516c77SSepherosa Ziehau 	free(sc->hn_tx_ring, M_DEVBUF);
435415516c77SSepherosa Ziehau 	sc->hn_tx_ring = NULL;
435515516c77SSepherosa Ziehau 
435615516c77SSepherosa Ziehau 	sc->hn_tx_ring_cnt = 0;
435715516c77SSepherosa Ziehau 	sc->hn_tx_ring_inuse = 0;
435815516c77SSepherosa Ziehau }
435915516c77SSepherosa Ziehau 
436023bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
436123bf9e15SSepherosa Ziehau 
436215516c77SSepherosa Ziehau static void
436315516c77SSepherosa Ziehau hn_start_taskfunc(void *xtxr, int pending __unused)
436415516c77SSepherosa Ziehau {
436515516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
436615516c77SSepherosa Ziehau 
436715516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
436815516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
436915516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
437015516c77SSepherosa Ziehau }
437115516c77SSepherosa Ziehau 
437223bf9e15SSepherosa Ziehau static int
437323bf9e15SSepherosa Ziehau hn_start_locked(struct hn_tx_ring *txr, int len)
437423bf9e15SSepherosa Ziehau {
437523bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
437623bf9e15SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
4377dc13fee6SSepherosa Ziehau 	int sched = 0;
437823bf9e15SSepherosa Ziehau 
437923bf9e15SSepherosa Ziehau 	KASSERT(hn_use_if_start,
438023bf9e15SSepherosa Ziehau 	    ("hn_start_locked is called, when if_start is disabled"));
438123bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
438223bf9e15SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
4383dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
438423bf9e15SSepherosa Ziehau 
438523bf9e15SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
4386dc13fee6SSepherosa Ziehau 		return (0);
438723bf9e15SSepherosa Ziehau 
438823bf9e15SSepherosa Ziehau 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
438923bf9e15SSepherosa Ziehau 	    IFF_DRV_RUNNING)
4390dc13fee6SSepherosa Ziehau 		return (0);
439123bf9e15SSepherosa Ziehau 
439223bf9e15SSepherosa Ziehau 	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
439323bf9e15SSepherosa Ziehau 		struct hn_txdesc *txd;
439423bf9e15SSepherosa Ziehau 		struct mbuf *m_head;
439523bf9e15SSepherosa Ziehau 		int error;
439623bf9e15SSepherosa Ziehau 
439723bf9e15SSepherosa Ziehau 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
439823bf9e15SSepherosa Ziehau 		if (m_head == NULL)
439923bf9e15SSepherosa Ziehau 			break;
440023bf9e15SSepherosa Ziehau 
440123bf9e15SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
440223bf9e15SSepherosa Ziehau 			/*
440323bf9e15SSepherosa Ziehau 			 * This sending could be time consuming; let callers
440423bf9e15SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
440523bf9e15SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
440623bf9e15SSepherosa Ziehau 			 */
440723bf9e15SSepherosa Ziehau 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
4408dc13fee6SSepherosa Ziehau 			sched = 1;
4409dc13fee6SSepherosa Ziehau 			break;
441023bf9e15SSepherosa Ziehau 		}
441123bf9e15SSepherosa Ziehau 
4412edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
4413edd3f315SSepherosa Ziehau 		if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
4414edd3f315SSepherosa Ziehau 			m_head = hn_tso_fixup(m_head);
4415edd3f315SSepherosa Ziehau 			if (__predict_false(m_head == NULL)) {
4416edd3f315SSepherosa Ziehau 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
4417edd3f315SSepherosa Ziehau 				continue;
4418edd3f315SSepherosa Ziehau 			}
4419edd3f315SSepherosa Ziehau 		}
4420edd3f315SSepherosa Ziehau #endif
4421edd3f315SSepherosa Ziehau 
442223bf9e15SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
442323bf9e15SSepherosa Ziehau 		if (txd == NULL) {
442423bf9e15SSepherosa Ziehau 			txr->hn_no_txdescs++;
442523bf9e15SSepherosa Ziehau 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
442623bf9e15SSepherosa Ziehau 			atomic_set_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
442723bf9e15SSepherosa Ziehau 			break;
442823bf9e15SSepherosa Ziehau 		}
442923bf9e15SSepherosa Ziehau 
4430dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
443123bf9e15SSepherosa Ziehau 		if (error) {
443223bf9e15SSepherosa Ziehau 			/* Both txd and m_head are freed */
4433dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
4434dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
443523bf9e15SSepherosa Ziehau 			continue;
443623bf9e15SSepherosa Ziehau 		}
443723bf9e15SSepherosa Ziehau 
4438dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
4439dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
4440dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
4441dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
4442dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
4443dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
4444dc13fee6SSepherosa Ziehau 					atomic_set_int(&ifp->if_drv_flags,
4445dc13fee6SSepherosa Ziehau 					    IFF_DRV_OACTIVE);
4446dc13fee6SSepherosa Ziehau 					break;
4447dc13fee6SSepherosa Ziehau 				}
4448dc13fee6SSepherosa Ziehau 			} else {
4449dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
445023bf9e15SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
445123bf9e15SSepherosa Ziehau 				if (__predict_false(error)) {
445223bf9e15SSepherosa Ziehau 					/* txd is freed, but m_head is not */
445323bf9e15SSepherosa Ziehau 					IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
4454dc13fee6SSepherosa Ziehau 					atomic_set_int(&ifp->if_drv_flags,
4455dc13fee6SSepherosa Ziehau 					    IFF_DRV_OACTIVE);
445623bf9e15SSepherosa Ziehau 					break;
445723bf9e15SSepherosa Ziehau 				}
445823bf9e15SSepherosa Ziehau 			}
4459dc13fee6SSepherosa Ziehau 		}
4460dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
4461dc13fee6SSepherosa Ziehau 		else {
4462dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
4463dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
4464dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
4465dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
4466dc13fee6SSepherosa Ziehau 		}
4467dc13fee6SSepherosa Ziehau #endif
4468dc13fee6SSepherosa Ziehau 	}
4469dc13fee6SSepherosa Ziehau 
4470dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
4471dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
4472dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
4473dc13fee6SSepherosa Ziehau 	return (sched);
447423bf9e15SSepherosa Ziehau }
447523bf9e15SSepherosa Ziehau 
447623bf9e15SSepherosa Ziehau static void
447723bf9e15SSepherosa Ziehau hn_start(struct ifnet *ifp)
447823bf9e15SSepherosa Ziehau {
447923bf9e15SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
448023bf9e15SSepherosa Ziehau 	struct hn_tx_ring *txr = &sc->hn_tx_ring[0];
448123bf9e15SSepherosa Ziehau 
448223bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
448323bf9e15SSepherosa Ziehau 		goto do_sched;
448423bf9e15SSepherosa Ziehau 
448523bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
448623bf9e15SSepherosa Ziehau 		int sched;
448723bf9e15SSepherosa Ziehau 
448823bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
448923bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
449023bf9e15SSepherosa Ziehau 		if (!sched)
449123bf9e15SSepherosa Ziehau 			return;
449223bf9e15SSepherosa Ziehau 	}
449323bf9e15SSepherosa Ziehau do_sched:
449423bf9e15SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
449523bf9e15SSepherosa Ziehau }
449623bf9e15SSepherosa Ziehau 
449715516c77SSepherosa Ziehau static void
449815516c77SSepherosa Ziehau hn_start_txeof_taskfunc(void *xtxr, int pending __unused)
449915516c77SSepherosa Ziehau {
450015516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
450115516c77SSepherosa Ziehau 
450215516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
450315516c77SSepherosa Ziehau 	atomic_clear_int(&txr->hn_sc->hn_ifp->if_drv_flags, IFF_DRV_OACTIVE);
450415516c77SSepherosa Ziehau 	hn_start_locked(txr, 0);
450515516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
450615516c77SSepherosa Ziehau }
450715516c77SSepherosa Ziehau 
450823bf9e15SSepherosa Ziehau static void
450923bf9e15SSepherosa Ziehau hn_start_txeof(struct hn_tx_ring *txr)
451023bf9e15SSepherosa Ziehau {
451123bf9e15SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
451223bf9e15SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
451323bf9e15SSepherosa Ziehau 
451423bf9e15SSepherosa Ziehau 	KASSERT(txr == &sc->hn_tx_ring[0], ("not the first TX ring"));
451523bf9e15SSepherosa Ziehau 
451623bf9e15SSepherosa Ziehau 	if (txr->hn_sched_tx)
451723bf9e15SSepherosa Ziehau 		goto do_sched;
451823bf9e15SSepherosa Ziehau 
451923bf9e15SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
452023bf9e15SSepherosa Ziehau 		int sched;
452123bf9e15SSepherosa Ziehau 
452223bf9e15SSepherosa Ziehau 		atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
452323bf9e15SSepherosa Ziehau 		sched = hn_start_locked(txr, txr->hn_direct_tx_size);
452423bf9e15SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
452523bf9e15SSepherosa Ziehau 		if (sched) {
452623bf9e15SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
452723bf9e15SSepherosa Ziehau 			    &txr->hn_tx_task);
452823bf9e15SSepherosa Ziehau 		}
452923bf9e15SSepherosa Ziehau 	} else {
453023bf9e15SSepherosa Ziehau do_sched:
453123bf9e15SSepherosa Ziehau 		/*
453223bf9e15SSepherosa Ziehau 		 * Release the OACTIVE earlier, with the hope, that
453323bf9e15SSepherosa Ziehau 		 * others could catch up.  The task will clear the
453423bf9e15SSepherosa Ziehau 		 * flag again with the hn_tx_lock to avoid possible
453523bf9e15SSepherosa Ziehau 		 * races.
453623bf9e15SSepherosa Ziehau 		 */
453723bf9e15SSepherosa Ziehau 		atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
453823bf9e15SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
453923bf9e15SSepherosa Ziehau 	}
454023bf9e15SSepherosa Ziehau }
454123bf9e15SSepherosa Ziehau 
454223bf9e15SSepherosa Ziehau #endif	/* HN_IFSTART_SUPPORT */
454323bf9e15SSepherosa Ziehau 
454415516c77SSepherosa Ziehau static int
454515516c77SSepherosa Ziehau hn_xmit(struct hn_tx_ring *txr, int len)
454615516c77SSepherosa Ziehau {
454715516c77SSepherosa Ziehau 	struct hn_softc *sc = txr->hn_sc;
454815516c77SSepherosa Ziehau 	struct ifnet *ifp = sc->hn_ifp;
454915516c77SSepherosa Ziehau 	struct mbuf *m_head;
4550dc13fee6SSepherosa Ziehau 	int sched = 0;
455115516c77SSepherosa Ziehau 
455215516c77SSepherosa Ziehau 	mtx_assert(&txr->hn_tx_lock, MA_OWNED);
455323bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
455415516c77SSepherosa Ziehau 	KASSERT(hn_use_if_start == 0,
455515516c77SSepherosa Ziehau 	    ("hn_xmit is called, when if_start is enabled"));
455623bf9e15SSepherosa Ziehau #endif
4557dc13fee6SSepherosa Ziehau 	KASSERT(txr->hn_agg_txd == NULL, ("lingering aggregating txdesc"));
455815516c77SSepherosa Ziehau 
455915516c77SSepherosa Ziehau 	if (__predict_false(txr->hn_suspended))
4560dc13fee6SSepherosa Ziehau 		return (0);
456115516c77SSepherosa Ziehau 
456215516c77SSepherosa Ziehau 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || txr->hn_oactive)
4563dc13fee6SSepherosa Ziehau 		return (0);
456415516c77SSepherosa Ziehau 
456515516c77SSepherosa Ziehau 	while ((m_head = drbr_peek(ifp, txr->hn_mbuf_br)) != NULL) {
456615516c77SSepherosa Ziehau 		struct hn_txdesc *txd;
456715516c77SSepherosa Ziehau 		int error;
456815516c77SSepherosa Ziehau 
456915516c77SSepherosa Ziehau 		if (len > 0 && m_head->m_pkthdr.len > len) {
457015516c77SSepherosa Ziehau 			/*
457115516c77SSepherosa Ziehau 			 * This sending could be time consuming; let callers
457215516c77SSepherosa Ziehau 			 * dispatch this packet sending (and sending of any
457315516c77SSepherosa Ziehau 			 * following up packets) to tx taskqueue.
457415516c77SSepherosa Ziehau 			 */
457515516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
4576dc13fee6SSepherosa Ziehau 			sched = 1;
4577dc13fee6SSepherosa Ziehau 			break;
457815516c77SSepherosa Ziehau 		}
457915516c77SSepherosa Ziehau 
458015516c77SSepherosa Ziehau 		txd = hn_txdesc_get(txr);
458115516c77SSepherosa Ziehau 		if (txd == NULL) {
458215516c77SSepherosa Ziehau 			txr->hn_no_txdescs++;
458315516c77SSepherosa Ziehau 			drbr_putback(ifp, txr->hn_mbuf_br, m_head);
458415516c77SSepherosa Ziehau 			txr->hn_oactive = 1;
458515516c77SSepherosa Ziehau 			break;
458615516c77SSepherosa Ziehau 		}
458715516c77SSepherosa Ziehau 
4588dc13fee6SSepherosa Ziehau 		error = hn_encap(ifp, txr, txd, &m_head);
458915516c77SSepherosa Ziehau 		if (error) {
459015516c77SSepherosa Ziehau 			/* Both txd and m_head are freed; discard */
4591dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd == NULL,
4592dc13fee6SSepherosa Ziehau 			    ("encap failed w/ pending aggregating txdesc"));
459315516c77SSepherosa Ziehau 			drbr_advance(ifp, txr->hn_mbuf_br);
459415516c77SSepherosa Ziehau 			continue;
459515516c77SSepherosa Ziehau 		}
459615516c77SSepherosa Ziehau 
4597dc13fee6SSepherosa Ziehau 		if (txr->hn_agg_pktleft == 0) {
4598dc13fee6SSepherosa Ziehau 			if (txr->hn_agg_txd != NULL) {
4599dc13fee6SSepherosa Ziehau 				KASSERT(m_head == NULL,
4600dc13fee6SSepherosa Ziehau 				    ("pending mbuf for aggregating txdesc"));
4601dc13fee6SSepherosa Ziehau 				error = hn_flush_txagg(ifp, txr);
460215516c77SSepherosa Ziehau 				if (__predict_false(error)) {
460315516c77SSepherosa Ziehau 					txr->hn_oactive = 1;
460415516c77SSepherosa Ziehau 					break;
460515516c77SSepherosa Ziehau 				}
4606dc13fee6SSepherosa Ziehau 			} else {
4607dc13fee6SSepherosa Ziehau 				KASSERT(m_head != NULL, ("mbuf was freed"));
4608dc13fee6SSepherosa Ziehau 				error = hn_txpkt(ifp, txr, txd);
4609dc13fee6SSepherosa Ziehau 				if (__predict_false(error)) {
4610dc13fee6SSepherosa Ziehau 					/* txd is freed, but m_head is not */
4611dc13fee6SSepherosa Ziehau 					drbr_putback(ifp, txr->hn_mbuf_br,
4612dc13fee6SSepherosa Ziehau 					    m_head);
4613dc13fee6SSepherosa Ziehau 					txr->hn_oactive = 1;
4614dc13fee6SSepherosa Ziehau 					break;
4615dc13fee6SSepherosa Ziehau 				}
4616dc13fee6SSepherosa Ziehau 			}
4617dc13fee6SSepherosa Ziehau 		}
4618dc13fee6SSepherosa Ziehau #ifdef INVARIANTS
4619dc13fee6SSepherosa Ziehau 		else {
4620dc13fee6SSepherosa Ziehau 			KASSERT(txr->hn_agg_txd != NULL,
4621dc13fee6SSepherosa Ziehau 			    ("no aggregating txdesc"));
4622dc13fee6SSepherosa Ziehau 			KASSERT(m_head == NULL,
4623dc13fee6SSepherosa Ziehau 			    ("pending mbuf for aggregating txdesc"));
4624dc13fee6SSepherosa Ziehau 		}
4625dc13fee6SSepherosa Ziehau #endif
462615516c77SSepherosa Ziehau 
462715516c77SSepherosa Ziehau 		/* Sent */
462815516c77SSepherosa Ziehau 		drbr_advance(ifp, txr->hn_mbuf_br);
462915516c77SSepherosa Ziehau 	}
4630dc13fee6SSepherosa Ziehau 
4631dc13fee6SSepherosa Ziehau 	/* Flush pending aggerated transmission. */
4632dc13fee6SSepherosa Ziehau 	if (txr->hn_agg_txd != NULL)
4633dc13fee6SSepherosa Ziehau 		hn_flush_txagg(ifp, txr);
4634dc13fee6SSepherosa Ziehau 	return (sched);
463515516c77SSepherosa Ziehau }
463615516c77SSepherosa Ziehau 
463715516c77SSepherosa Ziehau static int
463815516c77SSepherosa Ziehau hn_transmit(struct ifnet *ifp, struct mbuf *m)
463915516c77SSepherosa Ziehau {
464015516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
464115516c77SSepherosa Ziehau 	struct hn_tx_ring *txr;
464215516c77SSepherosa Ziehau 	int error, idx = 0;
464315516c77SSepherosa Ziehau 
4644edd3f315SSepherosa Ziehau #if defined(INET6) || defined(INET)
4645edd3f315SSepherosa Ziehau 	/*
4646edd3f315SSepherosa Ziehau 	 * Perform TSO packet header fixup now, since the TSO
4647edd3f315SSepherosa Ziehau 	 * packet header should be cache-hot.
4648edd3f315SSepherosa Ziehau 	 */
4649edd3f315SSepherosa Ziehau 	if (m->m_pkthdr.csum_flags & CSUM_TSO) {
4650edd3f315SSepherosa Ziehau 		m = hn_tso_fixup(m);
4651edd3f315SSepherosa Ziehau 		if (__predict_false(m == NULL)) {
4652edd3f315SSepherosa Ziehau 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
4653edd3f315SSepherosa Ziehau 			return EIO;
4654edd3f315SSepherosa Ziehau 		}
4655edd3f315SSepherosa Ziehau 	}
4656edd3f315SSepherosa Ziehau #endif
4657edd3f315SSepherosa Ziehau 
465815516c77SSepherosa Ziehau 	/*
465915516c77SSepherosa Ziehau 	 * Select the TX ring based on flowid
466015516c77SSepherosa Ziehau 	 */
466134d68912SSepherosa Ziehau 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
466234d68912SSepherosa Ziehau #ifdef RSS
466334d68912SSepherosa Ziehau 		uint32_t bid;
466434d68912SSepherosa Ziehau 
466534d68912SSepherosa Ziehau 		if (rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m),
466634d68912SSepherosa Ziehau 		    &bid) == 0)
466734d68912SSepherosa Ziehau 			idx = bid % sc->hn_tx_ring_inuse;
466834d68912SSepherosa Ziehau 		else
466934d68912SSepherosa Ziehau #endif
4670cc0c6ebcSSepherosa Ziehau 		{
4671cc0c6ebcSSepherosa Ziehau #if defined(INET6) || defined(INET)
4672cc0c6ebcSSepherosa Ziehau 			int tcpsyn = 0;
4673cc0c6ebcSSepherosa Ziehau 
4674cc0c6ebcSSepherosa Ziehau 			if (m->m_pkthdr.len < 128 &&
4675cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags &
4676cc0c6ebcSSepherosa Ziehau 			     (CSUM_IP_TCP | CSUM_IP6_TCP)) &&
4677cc0c6ebcSSepherosa Ziehau 			    (m->m_pkthdr.csum_flags & CSUM_TSO) == 0) {
4678cc0c6ebcSSepherosa Ziehau 				m = hn_check_tcpsyn(m, &tcpsyn);
4679cc0c6ebcSSepherosa Ziehau 				if (__predict_false(m == NULL)) {
4680cc0c6ebcSSepherosa Ziehau 					if_inc_counter(ifp,
4681cc0c6ebcSSepherosa Ziehau 					    IFCOUNTER_OERRORS, 1);
4682cc0c6ebcSSepherosa Ziehau 					return (EIO);
4683cc0c6ebcSSepherosa Ziehau 				}
4684cc0c6ebcSSepherosa Ziehau 			}
4685cc0c6ebcSSepherosa Ziehau #else
4686cc0c6ebcSSepherosa Ziehau 			const int tcpsyn = 0;
4687cc0c6ebcSSepherosa Ziehau #endif
4688cc0c6ebcSSepherosa Ziehau 			if (tcpsyn)
4689cc0c6ebcSSepherosa Ziehau 				idx = 0;
4690cc0c6ebcSSepherosa Ziehau 			else
469115516c77SSepherosa Ziehau 				idx = m->m_pkthdr.flowid % sc->hn_tx_ring_inuse;
469234d68912SSepherosa Ziehau 		}
4693cc0c6ebcSSepherosa Ziehau 	}
469415516c77SSepherosa Ziehau 	txr = &sc->hn_tx_ring[idx];
469515516c77SSepherosa Ziehau 
469615516c77SSepherosa Ziehau 	error = drbr_enqueue(ifp, txr->hn_mbuf_br, m);
469715516c77SSepherosa Ziehau 	if (error) {
469815516c77SSepherosa Ziehau 		if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
469915516c77SSepherosa Ziehau 		return error;
470015516c77SSepherosa Ziehau 	}
470115516c77SSepherosa Ziehau 
470215516c77SSepherosa Ziehau 	if (txr->hn_oactive)
470315516c77SSepherosa Ziehau 		return 0;
470415516c77SSepherosa Ziehau 
470515516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
470615516c77SSepherosa Ziehau 		goto do_sched;
470715516c77SSepherosa Ziehau 
470815516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
470915516c77SSepherosa Ziehau 		int sched;
471015516c77SSepherosa Ziehau 
471115516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
471215516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
471315516c77SSepherosa Ziehau 		if (!sched)
471415516c77SSepherosa Ziehau 			return 0;
471515516c77SSepherosa Ziehau 	}
471615516c77SSepherosa Ziehau do_sched:
471715516c77SSepherosa Ziehau 	taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_tx_task);
471815516c77SSepherosa Ziehau 	return 0;
471915516c77SSepherosa Ziehau }
472015516c77SSepherosa Ziehau 
472115516c77SSepherosa Ziehau static void
472215516c77SSepherosa Ziehau hn_tx_ring_qflush(struct hn_tx_ring *txr)
472315516c77SSepherosa Ziehau {
472415516c77SSepherosa Ziehau 	struct mbuf *m;
472515516c77SSepherosa Ziehau 
472615516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
472715516c77SSepherosa Ziehau 	while ((m = buf_ring_dequeue_sc(txr->hn_mbuf_br)) != NULL)
472815516c77SSepherosa Ziehau 		m_freem(m);
472915516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
473015516c77SSepherosa Ziehau }
473115516c77SSepherosa Ziehau 
473215516c77SSepherosa Ziehau static void
473315516c77SSepherosa Ziehau hn_xmit_qflush(struct ifnet *ifp)
473415516c77SSepherosa Ziehau {
473515516c77SSepherosa Ziehau 	struct hn_softc *sc = ifp->if_softc;
473615516c77SSepherosa Ziehau 	int i;
473715516c77SSepherosa Ziehau 
473815516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
473915516c77SSepherosa Ziehau 		hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
474015516c77SSepherosa Ziehau 	if_qflush(ifp);
474115516c77SSepherosa Ziehau }
474215516c77SSepherosa Ziehau 
474315516c77SSepherosa Ziehau static void
474415516c77SSepherosa Ziehau hn_xmit_txeof(struct hn_tx_ring *txr)
474515516c77SSepherosa Ziehau {
474615516c77SSepherosa Ziehau 
474715516c77SSepherosa Ziehau 	if (txr->hn_sched_tx)
474815516c77SSepherosa Ziehau 		goto do_sched;
474915516c77SSepherosa Ziehau 
475015516c77SSepherosa Ziehau 	if (mtx_trylock(&txr->hn_tx_lock)) {
475115516c77SSepherosa Ziehau 		int sched;
475215516c77SSepherosa Ziehau 
475315516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
475415516c77SSepherosa Ziehau 		sched = hn_xmit(txr, txr->hn_direct_tx_size);
475515516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
475615516c77SSepherosa Ziehau 		if (sched) {
475715516c77SSepherosa Ziehau 			taskqueue_enqueue(txr->hn_tx_taskq,
475815516c77SSepherosa Ziehau 			    &txr->hn_tx_task);
475915516c77SSepherosa Ziehau 		}
476015516c77SSepherosa Ziehau 	} else {
476115516c77SSepherosa Ziehau do_sched:
476215516c77SSepherosa Ziehau 		/*
476315516c77SSepherosa Ziehau 		 * Release the oactive earlier, with the hope, that
476415516c77SSepherosa Ziehau 		 * others could catch up.  The task will clear the
476515516c77SSepherosa Ziehau 		 * oactive again with the hn_tx_lock to avoid possible
476615516c77SSepherosa Ziehau 		 * races.
476715516c77SSepherosa Ziehau 		 */
476815516c77SSepherosa Ziehau 		txr->hn_oactive = 0;
476915516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
477015516c77SSepherosa Ziehau 	}
477115516c77SSepherosa Ziehau }
477215516c77SSepherosa Ziehau 
477315516c77SSepherosa Ziehau static void
477415516c77SSepherosa Ziehau hn_xmit_taskfunc(void *xtxr, int pending __unused)
477515516c77SSepherosa Ziehau {
477615516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
477715516c77SSepherosa Ziehau 
477815516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
477915516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
478015516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
478115516c77SSepherosa Ziehau }
478215516c77SSepherosa Ziehau 
478315516c77SSepherosa Ziehau static void
478415516c77SSepherosa Ziehau hn_xmit_txeof_taskfunc(void *xtxr, int pending __unused)
478515516c77SSepherosa Ziehau {
478615516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = xtxr;
478715516c77SSepherosa Ziehau 
478815516c77SSepherosa Ziehau 	mtx_lock(&txr->hn_tx_lock);
478915516c77SSepherosa Ziehau 	txr->hn_oactive = 0;
479015516c77SSepherosa Ziehau 	hn_xmit(txr, 0);
479115516c77SSepherosa Ziehau 	mtx_unlock(&txr->hn_tx_lock);
479215516c77SSepherosa Ziehau }
479315516c77SSepherosa Ziehau 
479415516c77SSepherosa Ziehau static int
479515516c77SSepherosa Ziehau hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
479615516c77SSepherosa Ziehau {
479715516c77SSepherosa Ziehau 	struct vmbus_chan_br cbr;
479815516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
479915516c77SSepherosa Ziehau 	struct hn_tx_ring *txr = NULL;
480015516c77SSepherosa Ziehau 	int idx, error;
480115516c77SSepherosa Ziehau 
480215516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
480315516c77SSepherosa Ziehau 
480415516c77SSepherosa Ziehau 	/*
480515516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
480615516c77SSepherosa Ziehau 	 */
480715516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
480815516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
480915516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
481015516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
481115516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED) == 0,
481215516c77SSepherosa Ziehau 	    ("RX ring %d already attached", idx));
481315516c77SSepherosa Ziehau 	rxr->hn_rx_flags |= HN_RX_FLAG_ATTACHED;
48143ab0fea1SDexuan Cui 	rxr->hn_chan = chan;
481515516c77SSepherosa Ziehau 
481615516c77SSepherosa Ziehau 	if (bootverbose) {
481715516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "link RX ring %d to chan%u\n",
481815516c77SSepherosa Ziehau 		    idx, vmbus_chan_id(chan));
481915516c77SSepherosa Ziehau 	}
482015516c77SSepherosa Ziehau 
482115516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
482215516c77SSepherosa Ziehau 		txr = &sc->hn_tx_ring[idx];
482315516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED) == 0,
482415516c77SSepherosa Ziehau 		    ("TX ring %d already attached", idx));
482515516c77SSepherosa Ziehau 		txr->hn_tx_flags |= HN_TX_FLAG_ATTACHED;
482615516c77SSepherosa Ziehau 
482715516c77SSepherosa Ziehau 		txr->hn_chan = chan;
482815516c77SSepherosa Ziehau 		if (bootverbose) {
482915516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "link TX ring %d to chan%u\n",
483015516c77SSepherosa Ziehau 			    idx, vmbus_chan_id(chan));
483115516c77SSepherosa Ziehau 		}
483215516c77SSepherosa Ziehau 	}
483315516c77SSepherosa Ziehau 
483415516c77SSepherosa Ziehau 	/* Bind this channel to a proper CPU. */
48350e11868dSSepherosa Ziehau 	vmbus_chan_cpu_set(chan, HN_RING_IDX2CPU(sc, idx));
483615516c77SSepherosa Ziehau 
483715516c77SSepherosa Ziehau 	/*
483815516c77SSepherosa Ziehau 	 * Open this channel
483915516c77SSepherosa Ziehau 	 */
484015516c77SSepherosa Ziehau 	cbr.cbr = rxr->hn_br;
484115516c77SSepherosa Ziehau 	cbr.cbr_paddr = rxr->hn_br_dma.hv_paddr;
484215516c77SSepherosa Ziehau 	cbr.cbr_txsz = HN_TXBR_SIZE;
484315516c77SSepherosa Ziehau 	cbr.cbr_rxsz = HN_RXBR_SIZE;
484415516c77SSepherosa Ziehau 	error = vmbus_chan_open_br(chan, &cbr, NULL, 0, hn_chan_callback, rxr);
484515516c77SSepherosa Ziehau 	if (error) {
484671e8ac56SSepherosa Ziehau 		if (error == EISCONN) {
484771e8ac56SSepherosa Ziehau 			if_printf(sc->hn_ifp, "bufring is connected after "
484871e8ac56SSepherosa Ziehau 			    "chan%u open failure\n", vmbus_chan_id(chan));
484971e8ac56SSepherosa Ziehau 			rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
485071e8ac56SSepherosa Ziehau 		} else {
485115516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "open chan%u failed: %d\n",
485215516c77SSepherosa Ziehau 			    vmbus_chan_id(chan), error);
485371e8ac56SSepherosa Ziehau 		}
485415516c77SSepherosa Ziehau 	}
485515516c77SSepherosa Ziehau 	return (error);
485615516c77SSepherosa Ziehau }
485715516c77SSepherosa Ziehau 
485815516c77SSepherosa Ziehau static void
485915516c77SSepherosa Ziehau hn_chan_detach(struct hn_softc *sc, struct vmbus_channel *chan)
486015516c77SSepherosa Ziehau {
486115516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr;
48622494d735SSepherosa Ziehau 	int idx, error;
486315516c77SSepherosa Ziehau 
486415516c77SSepherosa Ziehau 	idx = vmbus_chan_subidx(chan);
486515516c77SSepherosa Ziehau 
486615516c77SSepherosa Ziehau 	/*
486715516c77SSepherosa Ziehau 	 * Link this channel to RX/TX ring.
486815516c77SSepherosa Ziehau 	 */
486915516c77SSepherosa Ziehau 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
487015516c77SSepherosa Ziehau 	    ("invalid channel index %d, should > 0 && < %d",
487115516c77SSepherosa Ziehau 	     idx, sc->hn_rx_ring_inuse));
487215516c77SSepherosa Ziehau 	rxr = &sc->hn_rx_ring[idx];
487315516c77SSepherosa Ziehau 	KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED),
487415516c77SSepherosa Ziehau 	    ("RX ring %d is not attached", idx));
487515516c77SSepherosa Ziehau 	rxr->hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
487615516c77SSepherosa Ziehau 
487715516c77SSepherosa Ziehau 	if (idx < sc->hn_tx_ring_inuse) {
487815516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[idx];
487915516c77SSepherosa Ziehau 
488015516c77SSepherosa Ziehau 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED),
488115516c77SSepherosa Ziehau 		    ("TX ring %d is not attached attached", idx));
488215516c77SSepherosa Ziehau 		txr->hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
488315516c77SSepherosa Ziehau 	}
488415516c77SSepherosa Ziehau 
488515516c77SSepherosa Ziehau 	/*
488615516c77SSepherosa Ziehau 	 * Close this channel.
488715516c77SSepherosa Ziehau 	 *
488815516c77SSepherosa Ziehau 	 * NOTE:
488915516c77SSepherosa Ziehau 	 * Channel closing does _not_ destroy the target channel.
489015516c77SSepherosa Ziehau 	 */
48912494d735SSepherosa Ziehau 	error = vmbus_chan_close_direct(chan);
48922494d735SSepherosa Ziehau 	if (error == EISCONN) {
4893aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u bufring is connected "
4894aa1a2adcSSepherosa Ziehau 		    "after being closed\n", vmbus_chan_id(chan));
48952494d735SSepherosa Ziehau 		rxr->hn_rx_flags |= HN_RX_FLAG_BR_REF;
48962494d735SSepherosa Ziehau 	} else if (error) {
4897aa1a2adcSSepherosa Ziehau 		if_printf(sc->hn_ifp, "chan%u close failed: %d\n",
4898aa1a2adcSSepherosa Ziehau 		    vmbus_chan_id(chan), error);
48992494d735SSepherosa Ziehau 	}
490015516c77SSepherosa Ziehau }
490115516c77SSepherosa Ziehau 
490215516c77SSepherosa Ziehau static int
490315516c77SSepherosa Ziehau hn_attach_subchans(struct hn_softc *sc)
490415516c77SSepherosa Ziehau {
490515516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
490615516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
490715516c77SSepherosa Ziehau 	int i, error = 0;
490815516c77SSepherosa Ziehau 
490971e8ac56SSepherosa Ziehau 	KASSERT(subchan_cnt > 0, ("no sub-channels"));
491015516c77SSepherosa Ziehau 
491115516c77SSepherosa Ziehau 	/* Attach the sub-channels. */
491215516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
491315516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i) {
491471e8ac56SSepherosa Ziehau 		int error1;
491571e8ac56SSepherosa Ziehau 
491671e8ac56SSepherosa Ziehau 		error1 = hn_chan_attach(sc, subchans[i]);
491771e8ac56SSepherosa Ziehau 		if (error1) {
491871e8ac56SSepherosa Ziehau 			error = error1;
491971e8ac56SSepherosa Ziehau 			/* Move on; all channels will be detached later. */
492071e8ac56SSepherosa Ziehau 		}
492115516c77SSepherosa Ziehau 	}
492215516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
492315516c77SSepherosa Ziehau 
492415516c77SSepherosa Ziehau 	if (error) {
492515516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "sub-channels attach failed: %d\n", error);
492615516c77SSepherosa Ziehau 	} else {
492715516c77SSepherosa Ziehau 		if (bootverbose) {
492815516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "%d sub-channels attached\n",
492915516c77SSepherosa Ziehau 			    subchan_cnt);
493015516c77SSepherosa Ziehau 		}
493115516c77SSepherosa Ziehau 	}
493215516c77SSepherosa Ziehau 	return (error);
493315516c77SSepherosa Ziehau }
493415516c77SSepherosa Ziehau 
493515516c77SSepherosa Ziehau static void
493615516c77SSepherosa Ziehau hn_detach_allchans(struct hn_softc *sc)
493715516c77SSepherosa Ziehau {
493815516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
493915516c77SSepherosa Ziehau 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
494015516c77SSepherosa Ziehau 	int i;
494115516c77SSepherosa Ziehau 
494215516c77SSepherosa Ziehau 	if (subchan_cnt == 0)
494315516c77SSepherosa Ziehau 		goto back;
494415516c77SSepherosa Ziehau 
494515516c77SSepherosa Ziehau 	/* Detach the sub-channels. */
494615516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
494715516c77SSepherosa Ziehau 	for (i = 0; i < subchan_cnt; ++i)
494815516c77SSepherosa Ziehau 		hn_chan_detach(sc, subchans[i]);
494915516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, subchan_cnt);
495015516c77SSepherosa Ziehau 
495115516c77SSepherosa Ziehau back:
495215516c77SSepherosa Ziehau 	/*
495315516c77SSepherosa Ziehau 	 * Detach the primary channel, _after_ all sub-channels
495415516c77SSepherosa Ziehau 	 * are detached.
495515516c77SSepherosa Ziehau 	 */
495615516c77SSepherosa Ziehau 	hn_chan_detach(sc, sc->hn_prichan);
495715516c77SSepherosa Ziehau 
495815516c77SSepherosa Ziehau 	/* Wait for sub-channels to be destroyed, if any. */
495915516c77SSepherosa Ziehau 	vmbus_subchan_drain(sc->hn_prichan);
496015516c77SSepherosa Ziehau 
496115516c77SSepherosa Ziehau #ifdef INVARIANTS
496215516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
496315516c77SSepherosa Ziehau 		KASSERT((sc->hn_rx_ring[i].hn_rx_flags &
496415516c77SSepherosa Ziehau 		    HN_RX_FLAG_ATTACHED) == 0,
496515516c77SSepherosa Ziehau 		    ("%dth RX ring is still attached", i));
496615516c77SSepherosa Ziehau 	}
496715516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
496815516c77SSepherosa Ziehau 		KASSERT((sc->hn_tx_ring[i].hn_tx_flags &
496915516c77SSepherosa Ziehau 		    HN_TX_FLAG_ATTACHED) == 0,
497015516c77SSepherosa Ziehau 		    ("%dth TX ring is still attached", i));
497115516c77SSepherosa Ziehau 	}
497215516c77SSepherosa Ziehau #endif
497315516c77SSepherosa Ziehau }
497415516c77SSepherosa Ziehau 
497515516c77SSepherosa Ziehau static int
497615516c77SSepherosa Ziehau hn_synth_alloc_subchans(struct hn_softc *sc, int *nsubch)
497715516c77SSepherosa Ziehau {
497815516c77SSepherosa Ziehau 	struct vmbus_channel **subchans;
497915516c77SSepherosa Ziehau 	int nchan, rxr_cnt, error;
498015516c77SSepherosa Ziehau 
498115516c77SSepherosa Ziehau 	nchan = *nsubch + 1;
498215516c77SSepherosa Ziehau 	if (nchan == 1) {
498315516c77SSepherosa Ziehau 		/*
498415516c77SSepherosa Ziehau 		 * Multiple RX/TX rings are not requested.
498515516c77SSepherosa Ziehau 		 */
498615516c77SSepherosa Ziehau 		*nsubch = 0;
498715516c77SSepherosa Ziehau 		return (0);
498815516c77SSepherosa Ziehau 	}
498915516c77SSepherosa Ziehau 
499015516c77SSepherosa Ziehau 	/*
499115516c77SSepherosa Ziehau 	 * Query RSS capabilities, e.g. # of RX rings, and # of indirect
499215516c77SSepherosa Ziehau 	 * table entries.
499315516c77SSepherosa Ziehau 	 */
499415516c77SSepherosa Ziehau 	error = hn_rndis_query_rsscaps(sc, &rxr_cnt);
499515516c77SSepherosa Ziehau 	if (error) {
499615516c77SSepherosa Ziehau 		/* No RSS; this is benign. */
499715516c77SSepherosa Ziehau 		*nsubch = 0;
499815516c77SSepherosa Ziehau 		return (0);
499915516c77SSepherosa Ziehau 	}
500015516c77SSepherosa Ziehau 	if (bootverbose) {
500115516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
500215516c77SSepherosa Ziehau 		    rxr_cnt, nchan);
500315516c77SSepherosa Ziehau 	}
500415516c77SSepherosa Ziehau 
500515516c77SSepherosa Ziehau 	if (nchan > rxr_cnt)
500615516c77SSepherosa Ziehau 		nchan = rxr_cnt;
500715516c77SSepherosa Ziehau 	if (nchan == 1) {
500815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "only 1 channel is supported, no vRSS\n");
500915516c77SSepherosa Ziehau 		*nsubch = 0;
501015516c77SSepherosa Ziehau 		return (0);
501115516c77SSepherosa Ziehau 	}
501215516c77SSepherosa Ziehau 
501315516c77SSepherosa Ziehau 	/*
501415516c77SSepherosa Ziehau 	 * Allocate sub-channels from NVS.
501515516c77SSepherosa Ziehau 	 */
501615516c77SSepherosa Ziehau 	*nsubch = nchan - 1;
501715516c77SSepherosa Ziehau 	error = hn_nvs_alloc_subchans(sc, nsubch);
501815516c77SSepherosa Ziehau 	if (error || *nsubch == 0) {
501915516c77SSepherosa Ziehau 		/* Failed to allocate sub-channels. */
502015516c77SSepherosa Ziehau 		*nsubch = 0;
502115516c77SSepherosa Ziehau 		return (0);
502215516c77SSepherosa Ziehau 	}
502315516c77SSepherosa Ziehau 
502415516c77SSepherosa Ziehau 	/*
502515516c77SSepherosa Ziehau 	 * Wait for all sub-channels to become ready before moving on.
502615516c77SSepherosa Ziehau 	 */
502715516c77SSepherosa Ziehau 	subchans = vmbus_subchan_get(sc->hn_prichan, *nsubch);
502815516c77SSepherosa Ziehau 	vmbus_subchan_rel(subchans, *nsubch);
502915516c77SSepherosa Ziehau 	return (0);
503015516c77SSepherosa Ziehau }
503115516c77SSepherosa Ziehau 
50322494d735SSepherosa Ziehau static bool
50332494d735SSepherosa Ziehau hn_synth_attachable(const struct hn_softc *sc)
50342494d735SSepherosa Ziehau {
50352494d735SSepherosa Ziehau 	int i;
50362494d735SSepherosa Ziehau 
50372494d735SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_ERRORS)
50382494d735SSepherosa Ziehau 		return (false);
50392494d735SSepherosa Ziehau 
50402494d735SSepherosa Ziehau 	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
50412494d735SSepherosa Ziehau 		const struct hn_rx_ring *rxr = &sc->hn_rx_ring[i];
50422494d735SSepherosa Ziehau 
50432494d735SSepherosa Ziehau 		if (rxr->hn_rx_flags & HN_RX_FLAG_BR_REF)
50442494d735SSepherosa Ziehau 			return (false);
50452494d735SSepherosa Ziehau 	}
50462494d735SSepherosa Ziehau 	return (true);
50472494d735SSepherosa Ziehau }
50482494d735SSepherosa Ziehau 
5049b3b75d9cSSepherosa Ziehau /*
5050b3b75d9cSSepherosa Ziehau  * Make sure that the RX filter is zero after the successful
5051b3b75d9cSSepherosa Ziehau  * RNDIS initialization.
5052b3b75d9cSSepherosa Ziehau  *
5053b3b75d9cSSepherosa Ziehau  * NOTE:
5054b3b75d9cSSepherosa Ziehau  * Under certain conditions on certain versions of Hyper-V,
5055b3b75d9cSSepherosa Ziehau  * the RNDIS rxfilter is _not_ zero on the hypervisor side
5056b3b75d9cSSepherosa Ziehau  * after the successful RNDIS initialization, which breaks
5057b3b75d9cSSepherosa Ziehau  * the assumption of any following code (well, it breaks the
5058b3b75d9cSSepherosa Ziehau  * RNDIS API contract actually).  Clear the RNDIS rxfilter
5059b3b75d9cSSepherosa Ziehau  * explicitly, drain packets sneaking through, and drain the
5060b3b75d9cSSepherosa Ziehau  * interrupt taskqueues scheduled due to the stealth packets.
5061b3b75d9cSSepherosa Ziehau  */
5062b3b75d9cSSepherosa Ziehau static void
5063b3b75d9cSSepherosa Ziehau hn_rndis_init_fixat(struct hn_softc *sc, int nchan)
5064b3b75d9cSSepherosa Ziehau {
5065b3b75d9cSSepherosa Ziehau 
5066b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
5067b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, nchan);
5068b3b75d9cSSepherosa Ziehau }
5069b3b75d9cSSepherosa Ziehau 
507015516c77SSepherosa Ziehau static int
507115516c77SSepherosa Ziehau hn_synth_attach(struct hn_softc *sc, int mtu)
507215516c77SSepherosa Ziehau {
507371e8ac56SSepherosa Ziehau #define ATTACHED_NVS		0x0002
507471e8ac56SSepherosa Ziehau #define ATTACHED_RNDIS		0x0004
507571e8ac56SSepherosa Ziehau 
507615516c77SSepherosa Ziehau 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
5077b3b75d9cSSepherosa Ziehau 	int error, nsubch, nchan = 1, i, rndis_inited;
507871e8ac56SSepherosa Ziehau 	uint32_t old_caps, attached = 0;
507915516c77SSepherosa Ziehau 
508015516c77SSepherosa Ziehau 	KASSERT((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0,
508115516c77SSepherosa Ziehau 	    ("synthetic parts were attached"));
508215516c77SSepherosa Ziehau 
50832494d735SSepherosa Ziehau 	if (!hn_synth_attachable(sc))
50842494d735SSepherosa Ziehau 		return (ENXIO);
50852494d735SSepherosa Ziehau 
508615516c77SSepherosa Ziehau 	/* Save capabilities for later verification. */
508715516c77SSepherosa Ziehau 	old_caps = sc->hn_caps;
508815516c77SSepherosa Ziehau 	sc->hn_caps = 0;
508915516c77SSepherosa Ziehau 
509015516c77SSepherosa Ziehau 	/* Clear RSS stuffs. */
509115516c77SSepherosa Ziehau 	sc->hn_rss_ind_size = 0;
509215516c77SSepherosa Ziehau 	sc->hn_rss_hash = 0;
509315516c77SSepherosa Ziehau 
509415516c77SSepherosa Ziehau 	/*
509515516c77SSepherosa Ziehau 	 * Attach the primary channel _before_ attaching NVS and RNDIS.
509615516c77SSepherosa Ziehau 	 */
509715516c77SSepherosa Ziehau 	error = hn_chan_attach(sc, sc->hn_prichan);
509815516c77SSepherosa Ziehau 	if (error)
509971e8ac56SSepherosa Ziehau 		goto failed;
510015516c77SSepherosa Ziehau 
510115516c77SSepherosa Ziehau 	/*
510215516c77SSepherosa Ziehau 	 * Attach NVS.
510315516c77SSepherosa Ziehau 	 */
510415516c77SSepherosa Ziehau 	error = hn_nvs_attach(sc, mtu);
510515516c77SSepherosa Ziehau 	if (error)
510671e8ac56SSepherosa Ziehau 		goto failed;
510771e8ac56SSepherosa Ziehau 	attached |= ATTACHED_NVS;
510815516c77SSepherosa Ziehau 
510915516c77SSepherosa Ziehau 	/*
511015516c77SSepherosa Ziehau 	 * Attach RNDIS _after_ NVS is attached.
511115516c77SSepherosa Ziehau 	 */
5112b3b75d9cSSepherosa Ziehau 	error = hn_rndis_attach(sc, mtu, &rndis_inited);
5113b3b75d9cSSepherosa Ziehau 	if (rndis_inited)
5114b3b75d9cSSepherosa Ziehau 		attached |= ATTACHED_RNDIS;
511515516c77SSepherosa Ziehau 	if (error)
511671e8ac56SSepherosa Ziehau 		goto failed;
511715516c77SSepherosa Ziehau 
511815516c77SSepherosa Ziehau 	/*
511915516c77SSepherosa Ziehau 	 * Make sure capabilities are not changed.
512015516c77SSepherosa Ziehau 	 */
512115516c77SSepherosa Ziehau 	if (device_is_attached(sc->hn_dev) && old_caps != sc->hn_caps) {
512215516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "caps mismatch old 0x%08x, new 0x%08x\n",
512315516c77SSepherosa Ziehau 		    old_caps, sc->hn_caps);
512471e8ac56SSepherosa Ziehau 		error = ENXIO;
512571e8ac56SSepherosa Ziehau 		goto failed;
512615516c77SSepherosa Ziehau 	}
512715516c77SSepherosa Ziehau 
512815516c77SSepherosa Ziehau 	/*
512915516c77SSepherosa Ziehau 	 * Allocate sub-channels for multi-TX/RX rings.
513015516c77SSepherosa Ziehau 	 *
513115516c77SSepherosa Ziehau 	 * NOTE:
513215516c77SSepherosa Ziehau 	 * The # of RX rings that can be used is equivalent to the # of
513315516c77SSepherosa Ziehau 	 * channels to be requested.
513415516c77SSepherosa Ziehau 	 */
513515516c77SSepherosa Ziehau 	nsubch = sc->hn_rx_ring_cnt - 1;
513615516c77SSepherosa Ziehau 	error = hn_synth_alloc_subchans(sc, &nsubch);
513715516c77SSepherosa Ziehau 	if (error)
513871e8ac56SSepherosa Ziehau 		goto failed;
513971e8ac56SSepherosa Ziehau 	/* NOTE: _Full_ synthetic parts detach is required now. */
514071e8ac56SSepherosa Ziehau 	sc->hn_flags |= HN_FLAG_SYNTH_ATTACHED;
514115516c77SSepherosa Ziehau 
514271e8ac56SSepherosa Ziehau 	/*
514371e8ac56SSepherosa Ziehau 	 * Set the # of TX/RX rings that could be used according to
514471e8ac56SSepherosa Ziehau 	 * the # of channels that NVS offered.
514571e8ac56SSepherosa Ziehau 	 */
514615516c77SSepherosa Ziehau 	nchan = nsubch + 1;
514771e8ac56SSepherosa Ziehau 	hn_set_ring_inuse(sc, nchan);
514815516c77SSepherosa Ziehau 	if (nchan == 1) {
514915516c77SSepherosa Ziehau 		/* Only the primary channel can be used; done */
515015516c77SSepherosa Ziehau 		goto back;
515115516c77SSepherosa Ziehau 	}
515215516c77SSepherosa Ziehau 
515315516c77SSepherosa Ziehau 	/*
515471e8ac56SSepherosa Ziehau 	 * Attach the sub-channels.
5155afd4971bSSepherosa Ziehau 	 *
5156afd4971bSSepherosa Ziehau 	 * NOTE: hn_set_ring_inuse() _must_ have been called.
515715516c77SSepherosa Ziehau 	 */
515871e8ac56SSepherosa Ziehau 	error = hn_attach_subchans(sc);
515971e8ac56SSepherosa Ziehau 	if (error)
516071e8ac56SSepherosa Ziehau 		goto failed;
516115516c77SSepherosa Ziehau 
516271e8ac56SSepherosa Ziehau 	/*
516371e8ac56SSepherosa Ziehau 	 * Configure RSS key and indirect table _after_ all sub-channels
516471e8ac56SSepherosa Ziehau 	 * are attached.
516571e8ac56SSepherosa Ziehau 	 */
516615516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSKEY) == 0) {
516715516c77SSepherosa Ziehau 		/*
516815516c77SSepherosa Ziehau 		 * RSS key is not set yet; set it to the default RSS key.
516915516c77SSepherosa Ziehau 		 */
517015516c77SSepherosa Ziehau 		if (bootverbose)
517115516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS key\n");
517234d68912SSepherosa Ziehau #ifdef RSS
517334d68912SSepherosa Ziehau 		rss_getkey(rss->rss_key);
517434d68912SSepherosa Ziehau #else
517515516c77SSepherosa Ziehau 		memcpy(rss->rss_key, hn_rss_key_default, sizeof(rss->rss_key));
517634d68912SSepherosa Ziehau #endif
517715516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSKEY;
517815516c77SSepherosa Ziehau 	}
517915516c77SSepherosa Ziehau 
518015516c77SSepherosa Ziehau 	if ((sc->hn_flags & HN_FLAG_HAS_RSSIND) == 0) {
518115516c77SSepherosa Ziehau 		/*
518215516c77SSepherosa Ziehau 		 * RSS indirect table is not set yet; set it up in round-
518315516c77SSepherosa Ziehau 		 * robin fashion.
518415516c77SSepherosa Ziehau 		 */
518515516c77SSepherosa Ziehau 		if (bootverbose) {
518615516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "setup default RSS indirect "
518715516c77SSepherosa Ziehau 			    "table\n");
518815516c77SSepherosa Ziehau 		}
518934d68912SSepherosa Ziehau 		for (i = 0; i < NDIS_HASH_INDCNT; ++i) {
519034d68912SSepherosa Ziehau 			uint32_t subidx;
519134d68912SSepherosa Ziehau 
519234d68912SSepherosa Ziehau #ifdef RSS
519334d68912SSepherosa Ziehau 			subidx = rss_get_indirection_to_bucket(i);
519434d68912SSepherosa Ziehau #else
519534d68912SSepherosa Ziehau 			subidx = i;
519634d68912SSepherosa Ziehau #endif
519734d68912SSepherosa Ziehau 			rss->rss_ind[i] = subidx % nchan;
519834d68912SSepherosa Ziehau 		}
519915516c77SSepherosa Ziehau 		sc->hn_flags |= HN_FLAG_HAS_RSSIND;
520015516c77SSepherosa Ziehau 	} else {
520115516c77SSepherosa Ziehau 		/*
520215516c77SSepherosa Ziehau 		 * # of usable channels may be changed, so we have to
520315516c77SSepherosa Ziehau 		 * make sure that all entries in RSS indirect table
520415516c77SSepherosa Ziehau 		 * are valid.
5205afd4971bSSepherosa Ziehau 		 *
5206afd4971bSSepherosa Ziehau 		 * NOTE: hn_set_ring_inuse() _must_ have been called.
520715516c77SSepherosa Ziehau 		 */
5208afd4971bSSepherosa Ziehau 		hn_rss_ind_fixup(sc);
520915516c77SSepherosa Ziehau 	}
521015516c77SSepherosa Ziehau 
521115516c77SSepherosa Ziehau 	error = hn_rndis_conf_rss(sc, NDIS_RSS_FLAG_NONE);
521215516c77SSepherosa Ziehau 	if (error)
521371e8ac56SSepherosa Ziehau 		goto failed;
521471e8ac56SSepherosa Ziehau back:
5215dc13fee6SSepherosa Ziehau 	/*
5216dc13fee6SSepherosa Ziehau 	 * Fixup transmission aggregation setup.
5217dc13fee6SSepherosa Ziehau 	 */
5218dc13fee6SSepherosa Ziehau 	hn_set_txagg(sc);
5219b3b75d9cSSepherosa Ziehau 	hn_rndis_init_fixat(sc, nchan);
522015516c77SSepherosa Ziehau 	return (0);
522171e8ac56SSepherosa Ziehau 
522271e8ac56SSepherosa Ziehau failed:
522371e8ac56SSepherosa Ziehau 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
5224b3b75d9cSSepherosa Ziehau 		hn_rndis_init_fixat(sc, nchan);
522571e8ac56SSepherosa Ziehau 		hn_synth_detach(sc);
522671e8ac56SSepherosa Ziehau 	} else {
5227b3b75d9cSSepherosa Ziehau 		if (attached & ATTACHED_RNDIS) {
5228b3b75d9cSSepherosa Ziehau 			hn_rndis_init_fixat(sc, nchan);
522971e8ac56SSepherosa Ziehau 			hn_rndis_detach(sc);
5230b3b75d9cSSepherosa Ziehau 		}
523171e8ac56SSepherosa Ziehau 		if (attached & ATTACHED_NVS)
523271e8ac56SSepherosa Ziehau 			hn_nvs_detach(sc);
523371e8ac56SSepherosa Ziehau 		hn_chan_detach(sc, sc->hn_prichan);
523471e8ac56SSepherosa Ziehau 		/* Restore old capabilities. */
523571e8ac56SSepherosa Ziehau 		sc->hn_caps = old_caps;
523671e8ac56SSepherosa Ziehau 	}
523771e8ac56SSepherosa Ziehau 	return (error);
523871e8ac56SSepherosa Ziehau 
523971e8ac56SSepherosa Ziehau #undef ATTACHED_RNDIS
524071e8ac56SSepherosa Ziehau #undef ATTACHED_NVS
524115516c77SSepherosa Ziehau }
524215516c77SSepherosa Ziehau 
524315516c77SSepherosa Ziehau /*
524415516c77SSepherosa Ziehau  * NOTE:
524515516c77SSepherosa Ziehau  * The interface must have been suspended though hn_suspend(), before
524615516c77SSepherosa Ziehau  * this function get called.
524715516c77SSepherosa Ziehau  */
524815516c77SSepherosa Ziehau static void
524915516c77SSepherosa Ziehau hn_synth_detach(struct hn_softc *sc)
525015516c77SSepherosa Ziehau {
525115516c77SSepherosa Ziehau 
525215516c77SSepherosa Ziehau 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
525315516c77SSepherosa Ziehau 	    ("synthetic parts were not attached"));
525415516c77SSepherosa Ziehau 
525515516c77SSepherosa Ziehau 	/* Detach the RNDIS first. */
525615516c77SSepherosa Ziehau 	hn_rndis_detach(sc);
525715516c77SSepherosa Ziehau 
525815516c77SSepherosa Ziehau 	/* Detach NVS. */
525915516c77SSepherosa Ziehau 	hn_nvs_detach(sc);
526015516c77SSepherosa Ziehau 
526115516c77SSepherosa Ziehau 	/* Detach all of the channels. */
526215516c77SSepherosa Ziehau 	hn_detach_allchans(sc);
526315516c77SSepherosa Ziehau 
526415516c77SSepherosa Ziehau 	sc->hn_flags &= ~HN_FLAG_SYNTH_ATTACHED;
526515516c77SSepherosa Ziehau }
526615516c77SSepherosa Ziehau 
526715516c77SSepherosa Ziehau static void
526815516c77SSepherosa Ziehau hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt)
526915516c77SSepherosa Ziehau {
527015516c77SSepherosa Ziehau 	KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_cnt,
527115516c77SSepherosa Ziehau 	    ("invalid ring count %d", ring_cnt));
527215516c77SSepherosa Ziehau 
527315516c77SSepherosa Ziehau 	if (sc->hn_tx_ring_cnt > ring_cnt)
527415516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = ring_cnt;
527515516c77SSepherosa Ziehau 	else
527615516c77SSepherosa Ziehau 		sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
527715516c77SSepherosa Ziehau 	sc->hn_rx_ring_inuse = ring_cnt;
527815516c77SSepherosa Ziehau 
527934d68912SSepherosa Ziehau #ifdef RSS
528034d68912SSepherosa Ziehau 	if (sc->hn_rx_ring_inuse != rss_getnumbuckets()) {
528134d68912SSepherosa Ziehau 		if_printf(sc->hn_ifp, "# of RX rings (%d) does not match "
528234d68912SSepherosa Ziehau 		    "# of RSS buckets (%d)\n", sc->hn_rx_ring_inuse,
528334d68912SSepherosa Ziehau 		    rss_getnumbuckets());
528434d68912SSepherosa Ziehau 	}
528534d68912SSepherosa Ziehau #endif
528634d68912SSepherosa Ziehau 
528715516c77SSepherosa Ziehau 	if (bootverbose) {
528815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n",
528915516c77SSepherosa Ziehau 		    sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
529015516c77SSepherosa Ziehau 	}
529115516c77SSepherosa Ziehau }
529215516c77SSepherosa Ziehau 
529315516c77SSepherosa Ziehau static void
529425641fc7SSepherosa Ziehau hn_chan_drain(struct hn_softc *sc, struct vmbus_channel *chan)
529515516c77SSepherosa Ziehau {
529615516c77SSepherosa Ziehau 
529725641fc7SSepherosa Ziehau 	/*
529825641fc7SSepherosa Ziehau 	 * NOTE:
529925641fc7SSepherosa Ziehau 	 * The TX bufring will not be drained by the hypervisor,
530025641fc7SSepherosa Ziehau 	 * if the primary channel is revoked.
530125641fc7SSepherosa Ziehau 	 */
530225641fc7SSepherosa Ziehau 	while (!vmbus_chan_rx_empty(chan) ||
530325641fc7SSepherosa Ziehau 	    (!vmbus_chan_is_revoked(sc->hn_prichan) &&
530425641fc7SSepherosa Ziehau 	     !vmbus_chan_tx_empty(chan)))
530515516c77SSepherosa Ziehau 		pause("waitch", 1);
530615516c77SSepherosa Ziehau 	vmbus_chan_intr_drain(chan);
530715516c77SSepherosa Ziehau }
530815516c77SSepherosa Ziehau 
530915516c77SSepherosa Ziehau static void
5310b3b75d9cSSepherosa Ziehau hn_disable_rx(struct hn_softc *sc)
5311b3b75d9cSSepherosa Ziehau {
5312b3b75d9cSSepherosa Ziehau 
5313b3b75d9cSSepherosa Ziehau 	/*
5314b3b75d9cSSepherosa Ziehau 	 * Disable RX by clearing RX filter forcefully.
5315b3b75d9cSSepherosa Ziehau 	 */
5316b3b75d9cSSepherosa Ziehau 	sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE;
5317b3b75d9cSSepherosa Ziehau 	hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); /* ignore error */
5318b3b75d9cSSepherosa Ziehau 
5319b3b75d9cSSepherosa Ziehau 	/*
5320b3b75d9cSSepherosa Ziehau 	 * Give RNDIS enough time to flush all pending data packets.
5321b3b75d9cSSepherosa Ziehau 	 */
5322b3b75d9cSSepherosa Ziehau 	pause("waitrx", (200 * hz) / 1000);
5323b3b75d9cSSepherosa Ziehau }
5324b3b75d9cSSepherosa Ziehau 
5325b3b75d9cSSepherosa Ziehau /*
5326b3b75d9cSSepherosa Ziehau  * NOTE:
5327b3b75d9cSSepherosa Ziehau  * RX/TX _must_ have been suspended/disabled, before this function
5328b3b75d9cSSepherosa Ziehau  * is called.
5329b3b75d9cSSepherosa Ziehau  */
5330b3b75d9cSSepherosa Ziehau static void
5331b3b75d9cSSepherosa Ziehau hn_drain_rxtx(struct hn_softc *sc, int nchan)
533215516c77SSepherosa Ziehau {
533315516c77SSepherosa Ziehau 	struct vmbus_channel **subch = NULL;
5334b3b75d9cSSepherosa Ziehau 	int nsubch;
5335b3b75d9cSSepherosa Ziehau 
5336b3b75d9cSSepherosa Ziehau 	/*
5337b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX bufrings and interrupts.
5338b3b75d9cSSepherosa Ziehau 	 */
5339b3b75d9cSSepherosa Ziehau 	nsubch = nchan - 1;
5340b3b75d9cSSepherosa Ziehau 	if (nsubch > 0)
5341b3b75d9cSSepherosa Ziehau 		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
5342b3b75d9cSSepherosa Ziehau 
5343b3b75d9cSSepherosa Ziehau 	if (subch != NULL) {
5344b3b75d9cSSepherosa Ziehau 		int i;
5345b3b75d9cSSepherosa Ziehau 
5346b3b75d9cSSepherosa Ziehau 		for (i = 0; i < nsubch; ++i)
5347b3b75d9cSSepherosa Ziehau 			hn_chan_drain(sc, subch[i]);
5348b3b75d9cSSepherosa Ziehau 	}
5349b3b75d9cSSepherosa Ziehau 	hn_chan_drain(sc, sc->hn_prichan);
5350b3b75d9cSSepherosa Ziehau 
5351b3b75d9cSSepherosa Ziehau 	if (subch != NULL)
5352b3b75d9cSSepherosa Ziehau 		vmbus_subchan_rel(subch, nsubch);
5353b3b75d9cSSepherosa Ziehau }
5354b3b75d9cSSepherosa Ziehau 
5355b3b75d9cSSepherosa Ziehau static void
5356b3b75d9cSSepherosa Ziehau hn_suspend_data(struct hn_softc *sc)
5357b3b75d9cSSepherosa Ziehau {
535825641fc7SSepherosa Ziehau 	struct hn_tx_ring *txr;
5359b3b75d9cSSepherosa Ziehau 	int i;
536015516c77SSepherosa Ziehau 
536115516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
536215516c77SSepherosa Ziehau 
536315516c77SSepherosa Ziehau 	/*
536415516c77SSepherosa Ziehau 	 * Suspend TX.
536515516c77SSepherosa Ziehau 	 */
536615516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
536725641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
536815516c77SSepherosa Ziehau 
536915516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
537015516c77SSepherosa Ziehau 		txr->hn_suspended = 1;
537115516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
537215516c77SSepherosa Ziehau 		/* No one is able send more packets now. */
537315516c77SSepherosa Ziehau 
537425641fc7SSepherosa Ziehau 		/*
537525641fc7SSepherosa Ziehau 		 * Wait for all pending sends to finish.
537625641fc7SSepherosa Ziehau 		 *
537725641fc7SSepherosa Ziehau 		 * NOTE:
537825641fc7SSepherosa Ziehau 		 * We will _not_ receive all pending send-done, if the
537925641fc7SSepherosa Ziehau 		 * primary channel is revoked.
538025641fc7SSepherosa Ziehau 		 */
538125641fc7SSepherosa Ziehau 		while (hn_tx_ring_pending(txr) &&
538225641fc7SSepherosa Ziehau 		    !vmbus_chan_is_revoked(sc->hn_prichan))
538315516c77SSepherosa Ziehau 			pause("hnwtx", 1 /* 1 tick */);
538415516c77SSepherosa Ziehau 	}
538515516c77SSepherosa Ziehau 
538615516c77SSepherosa Ziehau 	/*
5387b3b75d9cSSepherosa Ziehau 	 * Disable RX.
538815516c77SSepherosa Ziehau 	 */
5389b3b75d9cSSepherosa Ziehau 	hn_disable_rx(sc);
539015516c77SSepherosa Ziehau 
539115516c77SSepherosa Ziehau 	/*
5392b3b75d9cSSepherosa Ziehau 	 * Drain RX/TX.
539315516c77SSepherosa Ziehau 	 */
5394b3b75d9cSSepherosa Ziehau 	hn_drain_rxtx(sc, sc->hn_rx_ring_inuse);
539525641fc7SSepherosa Ziehau 
539625641fc7SSepherosa Ziehau 	/*
539725641fc7SSepherosa Ziehau 	 * Drain any pending TX tasks.
539825641fc7SSepherosa Ziehau 	 *
539925641fc7SSepherosa Ziehau 	 * NOTE:
5400b3b75d9cSSepherosa Ziehau 	 * The above hn_drain_rxtx() can dispatch TX tasks, so the TX
5401b3b75d9cSSepherosa Ziehau 	 * tasks will have to be drained _after_ the above hn_drain_rxtx().
540225641fc7SSepherosa Ziehau 	 */
540325641fc7SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
540425641fc7SSepherosa Ziehau 		txr = &sc->hn_tx_ring[i];
540525641fc7SSepherosa Ziehau 
540625641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_tx_task);
540725641fc7SSepherosa Ziehau 		taskqueue_drain(txr->hn_tx_taskq, &txr->hn_txeof_task);
540825641fc7SSepherosa Ziehau 	}
540915516c77SSepherosa Ziehau }
541015516c77SSepherosa Ziehau 
541115516c77SSepherosa Ziehau static void
541215516c77SSepherosa Ziehau hn_suspend_mgmt_taskfunc(void *xsc, int pending __unused)
541315516c77SSepherosa Ziehau {
541415516c77SSepherosa Ziehau 
541515516c77SSepherosa Ziehau 	((struct hn_softc *)xsc)->hn_mgmt_taskq = NULL;
541615516c77SSepherosa Ziehau }
541715516c77SSepherosa Ziehau 
541815516c77SSepherosa Ziehau static void
541915516c77SSepherosa Ziehau hn_suspend_mgmt(struct hn_softc *sc)
542015516c77SSepherosa Ziehau {
542115516c77SSepherosa Ziehau 	struct task task;
542215516c77SSepherosa Ziehau 
542315516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
542415516c77SSepherosa Ziehau 
542515516c77SSepherosa Ziehau 	/*
542615516c77SSepherosa Ziehau 	 * Make sure that hn_mgmt_taskq0 can nolonger be accessed
542715516c77SSepherosa Ziehau 	 * through hn_mgmt_taskq.
542815516c77SSepherosa Ziehau 	 */
542915516c77SSepherosa Ziehau 	TASK_INIT(&task, 0, hn_suspend_mgmt_taskfunc, sc);
543015516c77SSepherosa Ziehau 	vmbus_chan_run_task(sc->hn_prichan, &task);
543115516c77SSepherosa Ziehau 
543215516c77SSepherosa Ziehau 	/*
543315516c77SSepherosa Ziehau 	 * Make sure that all pending management tasks are completed.
543415516c77SSepherosa Ziehau 	 */
543515516c77SSepherosa Ziehau 	taskqueue_drain(sc->hn_mgmt_taskq0, &sc->hn_netchg_init);
543615516c77SSepherosa Ziehau 	taskqueue_drain_timeout(sc->hn_mgmt_taskq0, &sc->hn_netchg_status);
543715516c77SSepherosa Ziehau 	taskqueue_drain_all(sc->hn_mgmt_taskq0);
543815516c77SSepherosa Ziehau }
543915516c77SSepherosa Ziehau 
544015516c77SSepherosa Ziehau static void
544115516c77SSepherosa Ziehau hn_suspend(struct hn_softc *sc)
544215516c77SSepherosa Ziehau {
544315516c77SSepherosa Ziehau 
544487f8129dSSepherosa Ziehau 	/* Disable polling. */
544587f8129dSSepherosa Ziehau 	hn_polling(sc, 0);
544687f8129dSSepherosa Ziehau 
54475bdfd3fdSDexuan Cui 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
54485bdfd3fdSDexuan Cui 	    (sc->hn_flags & HN_FLAG_VF))
544915516c77SSepherosa Ziehau 		hn_suspend_data(sc);
545015516c77SSepherosa Ziehau 	hn_suspend_mgmt(sc);
545115516c77SSepherosa Ziehau }
545215516c77SSepherosa Ziehau 
545315516c77SSepherosa Ziehau static void
545415516c77SSepherosa Ziehau hn_resume_tx(struct hn_softc *sc, int tx_ring_cnt)
545515516c77SSepherosa Ziehau {
545615516c77SSepherosa Ziehau 	int i;
545715516c77SSepherosa Ziehau 
545815516c77SSepherosa Ziehau 	KASSERT(tx_ring_cnt <= sc->hn_tx_ring_cnt,
545915516c77SSepherosa Ziehau 	    ("invalid TX ring count %d", tx_ring_cnt));
546015516c77SSepherosa Ziehau 
546115516c77SSepherosa Ziehau 	for (i = 0; i < tx_ring_cnt; ++i) {
546215516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
546315516c77SSepherosa Ziehau 
546415516c77SSepherosa Ziehau 		mtx_lock(&txr->hn_tx_lock);
546515516c77SSepherosa Ziehau 		txr->hn_suspended = 0;
546615516c77SSepherosa Ziehau 		mtx_unlock(&txr->hn_tx_lock);
546715516c77SSepherosa Ziehau 	}
546815516c77SSepherosa Ziehau }
546915516c77SSepherosa Ziehau 
547015516c77SSepherosa Ziehau static void
547115516c77SSepherosa Ziehau hn_resume_data(struct hn_softc *sc)
547215516c77SSepherosa Ziehau {
547315516c77SSepherosa Ziehau 	int i;
547415516c77SSepherosa Ziehau 
547515516c77SSepherosa Ziehau 	HN_LOCK_ASSERT(sc);
547615516c77SSepherosa Ziehau 
547715516c77SSepherosa Ziehau 	/*
547815516c77SSepherosa Ziehau 	 * Re-enable RX.
547915516c77SSepherosa Ziehau 	 */
5480c08f7b2cSSepherosa Ziehau 	hn_rxfilter_config(sc);
548115516c77SSepherosa Ziehau 
548215516c77SSepherosa Ziehau 	/*
548315516c77SSepherosa Ziehau 	 * Make sure to clear suspend status on "all" TX rings,
548415516c77SSepherosa Ziehau 	 * since hn_tx_ring_inuse can be changed after
548515516c77SSepherosa Ziehau 	 * hn_suspend_data().
548615516c77SSepherosa Ziehau 	 */
548715516c77SSepherosa Ziehau 	hn_resume_tx(sc, sc->hn_tx_ring_cnt);
548815516c77SSepherosa Ziehau 
548923bf9e15SSepherosa Ziehau #ifdef HN_IFSTART_SUPPORT
549023bf9e15SSepherosa Ziehau 	if (!hn_use_if_start)
549123bf9e15SSepherosa Ziehau #endif
549223bf9e15SSepherosa Ziehau 	{
549315516c77SSepherosa Ziehau 		/*
549415516c77SSepherosa Ziehau 		 * Flush unused drbrs, since hn_tx_ring_inuse may be
549515516c77SSepherosa Ziehau 		 * reduced.
549615516c77SSepherosa Ziehau 		 */
549715516c77SSepherosa Ziehau 		for (i = sc->hn_tx_ring_inuse; i < sc->hn_tx_ring_cnt; ++i)
549815516c77SSepherosa Ziehau 			hn_tx_ring_qflush(&sc->hn_tx_ring[i]);
549915516c77SSepherosa Ziehau 	}
550015516c77SSepherosa Ziehau 
550115516c77SSepherosa Ziehau 	/*
550215516c77SSepherosa Ziehau 	 * Kick start TX.
550315516c77SSepherosa Ziehau 	 */
550415516c77SSepherosa Ziehau 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i) {
550515516c77SSepherosa Ziehau 		struct hn_tx_ring *txr = &sc->hn_tx_ring[i];
550615516c77SSepherosa Ziehau 
550715516c77SSepherosa Ziehau 		/*
550815516c77SSepherosa Ziehau 		 * Use txeof task, so that any pending oactive can be
550915516c77SSepherosa Ziehau 		 * cleared properly.
551015516c77SSepherosa Ziehau 		 */
551115516c77SSepherosa Ziehau 		taskqueue_enqueue(txr->hn_tx_taskq, &txr->hn_txeof_task);
551215516c77SSepherosa Ziehau 	}
551315516c77SSepherosa Ziehau }
551415516c77SSepherosa Ziehau 
551515516c77SSepherosa Ziehau static void
551615516c77SSepherosa Ziehau hn_resume_mgmt(struct hn_softc *sc)
551715516c77SSepherosa Ziehau {
551815516c77SSepherosa Ziehau 
551915516c77SSepherosa Ziehau 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
552015516c77SSepherosa Ziehau 
552115516c77SSepherosa Ziehau 	/*
552215516c77SSepherosa Ziehau 	 * Kick off network change detection, if it was pending.
552315516c77SSepherosa Ziehau 	 * If no network change was pending, start link status
552415516c77SSepherosa Ziehau 	 * checks, which is more lightweight than network change
552515516c77SSepherosa Ziehau 	 * detection.
552615516c77SSepherosa Ziehau 	 */
552715516c77SSepherosa Ziehau 	if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
552815516c77SSepherosa Ziehau 		hn_change_network(sc);
552915516c77SSepherosa Ziehau 	else
553015516c77SSepherosa Ziehau 		hn_update_link_status(sc);
553115516c77SSepherosa Ziehau }
553215516c77SSepherosa Ziehau 
553315516c77SSepherosa Ziehau static void
553415516c77SSepherosa Ziehau hn_resume(struct hn_softc *sc)
553515516c77SSepherosa Ziehau {
553615516c77SSepherosa Ziehau 
55375bdfd3fdSDexuan Cui 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
55385bdfd3fdSDexuan Cui 	    (sc->hn_flags & HN_FLAG_VF))
553915516c77SSepherosa Ziehau 		hn_resume_data(sc);
55405bdfd3fdSDexuan Cui 
55415bdfd3fdSDexuan Cui 	/*
55425bdfd3fdSDexuan Cui 	 * When the VF is activated, the synthetic interface is changed
55435bdfd3fdSDexuan Cui 	 * to DOWN in hn_set_vf(). Here, if the VF is still active, we
55445bdfd3fdSDexuan Cui 	 * don't call hn_resume_mgmt() until the VF is deactivated in
55455bdfd3fdSDexuan Cui 	 * hn_set_vf().
55465bdfd3fdSDexuan Cui 	 */
55475bdfd3fdSDexuan Cui 	if (!(sc->hn_flags & HN_FLAG_VF))
554815516c77SSepherosa Ziehau 		hn_resume_mgmt(sc);
554987f8129dSSepherosa Ziehau 
555087f8129dSSepherosa Ziehau 	/*
555187f8129dSSepherosa Ziehau 	 * Re-enable polling if this interface is running and
555287f8129dSSepherosa Ziehau 	 * the polling is requested.
555387f8129dSSepherosa Ziehau 	 */
555487f8129dSSepherosa Ziehau 	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->hn_pollhz > 0)
555587f8129dSSepherosa Ziehau 		hn_polling(sc, sc->hn_pollhz);
555615516c77SSepherosa Ziehau }
555715516c77SSepherosa Ziehau 
555815516c77SSepherosa Ziehau static void
555915516c77SSepherosa Ziehau hn_rndis_rx_status(struct hn_softc *sc, const void *data, int dlen)
556015516c77SSepherosa Ziehau {
556115516c77SSepherosa Ziehau 	const struct rndis_status_msg *msg;
556215516c77SSepherosa Ziehau 	int ofs;
556315516c77SSepherosa Ziehau 
556415516c77SSepherosa Ziehau 	if (dlen < sizeof(*msg)) {
556515516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid RNDIS status\n");
556615516c77SSepherosa Ziehau 		return;
556715516c77SSepherosa Ziehau 	}
556815516c77SSepherosa Ziehau 	msg = data;
556915516c77SSepherosa Ziehau 
557015516c77SSepherosa Ziehau 	switch (msg->rm_status) {
557115516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_CONNECT:
557215516c77SSepherosa Ziehau 	case RNDIS_STATUS_MEDIA_DISCONNECT:
557315516c77SSepherosa Ziehau 		hn_update_link_status(sc);
557415516c77SSepherosa Ziehau 		break;
557515516c77SSepherosa Ziehau 
557615516c77SSepherosa Ziehau 	case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
5577*40905afaSSepherosa Ziehau 	case RNDIS_STATUS_LINK_SPEED_CHANGE:
557815516c77SSepherosa Ziehau 		/* Not really useful; ignore. */
557915516c77SSepherosa Ziehau 		break;
558015516c77SSepherosa Ziehau 
558115516c77SSepherosa Ziehau 	case RNDIS_STATUS_NETWORK_CHANGE:
558215516c77SSepherosa Ziehau 		ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
558315516c77SSepherosa Ziehau 		if (dlen < ofs + msg->rm_stbuflen ||
558415516c77SSepherosa Ziehau 		    msg->rm_stbuflen < sizeof(uint32_t)) {
558515516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed\n");
558615516c77SSepherosa Ziehau 		} else {
558715516c77SSepherosa Ziehau 			uint32_t change;
558815516c77SSepherosa Ziehau 
558915516c77SSepherosa Ziehau 			memcpy(&change, ((const uint8_t *)msg) + ofs,
559015516c77SSepherosa Ziehau 			    sizeof(change));
559115516c77SSepherosa Ziehau 			if_printf(sc->hn_ifp, "network changed, change %u\n",
559215516c77SSepherosa Ziehau 			    change);
559315516c77SSepherosa Ziehau 		}
559415516c77SSepherosa Ziehau 		hn_change_network(sc);
559515516c77SSepherosa Ziehau 		break;
559615516c77SSepherosa Ziehau 
559715516c77SSepherosa Ziehau 	default:
559815516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
559915516c77SSepherosa Ziehau 		    msg->rm_status);
560015516c77SSepherosa Ziehau 		break;
560115516c77SSepherosa Ziehau 	}
560215516c77SSepherosa Ziehau }
560315516c77SSepherosa Ziehau 
560415516c77SSepherosa Ziehau static int
560515516c77SSepherosa Ziehau hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info)
560615516c77SSepherosa Ziehau {
560715516c77SSepherosa Ziehau 	const struct rndis_pktinfo *pi = info_data;
560815516c77SSepherosa Ziehau 	uint32_t mask = 0;
560915516c77SSepherosa Ziehau 
561015516c77SSepherosa Ziehau 	while (info_dlen != 0) {
561115516c77SSepherosa Ziehau 		const void *data;
561215516c77SSepherosa Ziehau 		uint32_t dlen;
561315516c77SSepherosa Ziehau 
561415516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < sizeof(*pi)))
561515516c77SSepherosa Ziehau 			return (EINVAL);
561615516c77SSepherosa Ziehau 		if (__predict_false(info_dlen < pi->rm_size))
561715516c77SSepherosa Ziehau 			return (EINVAL);
561815516c77SSepherosa Ziehau 		info_dlen -= pi->rm_size;
561915516c77SSepherosa Ziehau 
562015516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
562115516c77SSepherosa Ziehau 			return (EINVAL);
562215516c77SSepherosa Ziehau 		if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
562315516c77SSepherosa Ziehau 			return (EINVAL);
562415516c77SSepherosa Ziehau 		dlen = pi->rm_size - pi->rm_pktinfooffset;
562515516c77SSepherosa Ziehau 		data = pi->rm_data;
562615516c77SSepherosa Ziehau 
562715516c77SSepherosa Ziehau 		switch (pi->rm_type) {
562815516c77SSepherosa Ziehau 		case NDIS_PKTINFO_TYPE_VLAN:
562915516c77SSepherosa Ziehau 			if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
563015516c77SSepherosa Ziehau 				return (EINVAL);
563115516c77SSepherosa Ziehau 			info->vlan_info = *((const uint32_t *)data);
563215516c77SSepherosa Ziehau 			mask |= HN_RXINFO_VLAN;
563315516c77SSepherosa Ziehau 			break;
563415516c77SSepherosa Ziehau 
563515516c77SSepherosa Ziehau 		case NDIS_PKTINFO_TYPE_CSUM:
563615516c77SSepherosa Ziehau 			if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
563715516c77SSepherosa Ziehau 				return (EINVAL);
563815516c77SSepherosa Ziehau 			info->csum_info = *((const uint32_t *)data);
563915516c77SSepherosa Ziehau 			mask |= HN_RXINFO_CSUM;
564015516c77SSepherosa Ziehau 			break;
564115516c77SSepherosa Ziehau 
564215516c77SSepherosa Ziehau 		case HN_NDIS_PKTINFO_TYPE_HASHVAL:
564315516c77SSepherosa Ziehau 			if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
564415516c77SSepherosa Ziehau 				return (EINVAL);
564515516c77SSepherosa Ziehau 			info->hash_value = *((const uint32_t *)data);
564615516c77SSepherosa Ziehau 			mask |= HN_RXINFO_HASHVAL;
564715516c77SSepherosa Ziehau 			break;
564815516c77SSepherosa Ziehau 
564915516c77SSepherosa Ziehau 		case HN_NDIS_PKTINFO_TYPE_HASHINF:
565015516c77SSepherosa Ziehau 			if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
565115516c77SSepherosa Ziehau 				return (EINVAL);
565215516c77SSepherosa Ziehau 			info->hash_info = *((const uint32_t *)data);
565315516c77SSepherosa Ziehau 			mask |= HN_RXINFO_HASHINF;
565415516c77SSepherosa Ziehau 			break;
565515516c77SSepherosa Ziehau 
565615516c77SSepherosa Ziehau 		default:
565715516c77SSepherosa Ziehau 			goto next;
565815516c77SSepherosa Ziehau 		}
565915516c77SSepherosa Ziehau 
566015516c77SSepherosa Ziehau 		if (mask == HN_RXINFO_ALL) {
566115516c77SSepherosa Ziehau 			/* All found; done */
566215516c77SSepherosa Ziehau 			break;
566315516c77SSepherosa Ziehau 		}
566415516c77SSepherosa Ziehau next:
566515516c77SSepherosa Ziehau 		pi = (const struct rndis_pktinfo *)
566615516c77SSepherosa Ziehau 		    ((const uint8_t *)pi + pi->rm_size);
566715516c77SSepherosa Ziehau 	}
566815516c77SSepherosa Ziehau 
566915516c77SSepherosa Ziehau 	/*
567015516c77SSepherosa Ziehau 	 * Final fixup.
567115516c77SSepherosa Ziehau 	 * - If there is no hash value, invalidate the hash info.
567215516c77SSepherosa Ziehau 	 */
567315516c77SSepherosa Ziehau 	if ((mask & HN_RXINFO_HASHVAL) == 0)
567415516c77SSepherosa Ziehau 		info->hash_info = HN_NDIS_HASH_INFO_INVALID;
567515516c77SSepherosa Ziehau 	return (0);
567615516c77SSepherosa Ziehau }
567715516c77SSepherosa Ziehau 
567815516c77SSepherosa Ziehau static __inline bool
567915516c77SSepherosa Ziehau hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
568015516c77SSepherosa Ziehau {
568115516c77SSepherosa Ziehau 
568215516c77SSepherosa Ziehau 	if (off < check_off) {
568315516c77SSepherosa Ziehau 		if (__predict_true(off + len <= check_off))
568415516c77SSepherosa Ziehau 			return (false);
568515516c77SSepherosa Ziehau 	} else if (off > check_off) {
568615516c77SSepherosa Ziehau 		if (__predict_true(check_off + check_len <= off))
568715516c77SSepherosa Ziehau 			return (false);
568815516c77SSepherosa Ziehau 	}
568915516c77SSepherosa Ziehau 	return (true);
569015516c77SSepherosa Ziehau }
569115516c77SSepherosa Ziehau 
569215516c77SSepherosa Ziehau static void
569315516c77SSepherosa Ziehau hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen)
569415516c77SSepherosa Ziehau {
569515516c77SSepherosa Ziehau 	const struct rndis_packet_msg *pkt;
569615516c77SSepherosa Ziehau 	struct hn_rxinfo info;
569715516c77SSepherosa Ziehau 	int data_off, pktinfo_off, data_len, pktinfo_len;
569815516c77SSepherosa Ziehau 
569915516c77SSepherosa Ziehau 	/*
570015516c77SSepherosa Ziehau 	 * Check length.
570115516c77SSepherosa Ziehau 	 */
570215516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*pkt))) {
570315516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
570415516c77SSepherosa Ziehau 		return;
570515516c77SSepherosa Ziehau 	}
570615516c77SSepherosa Ziehau 	pkt = data;
570715516c77SSepherosa Ziehau 
570815516c77SSepherosa Ziehau 	if (__predict_false(dlen < pkt->rm_len)) {
570915516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
571015516c77SSepherosa Ziehau 		    "dlen %d, msglen %u\n", dlen, pkt->rm_len);
571115516c77SSepherosa Ziehau 		return;
571215516c77SSepherosa Ziehau 	}
571315516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_len <
571415516c77SSepherosa Ziehau 	    pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
571515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
571615516c77SSepherosa Ziehau 		    "msglen %u, data %u, oob %u, pktinfo %u\n",
571715516c77SSepherosa Ziehau 		    pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
571815516c77SSepherosa Ziehau 		    pkt->rm_pktinfolen);
571915516c77SSepherosa Ziehau 		return;
572015516c77SSepherosa Ziehau 	}
572115516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_datalen == 0)) {
572215516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
572315516c77SSepherosa Ziehau 		return;
572415516c77SSepherosa Ziehau 	}
572515516c77SSepherosa Ziehau 
572615516c77SSepherosa Ziehau 	/*
572715516c77SSepherosa Ziehau 	 * Check offests.
572815516c77SSepherosa Ziehau 	 */
572915516c77SSepherosa Ziehau #define IS_OFFSET_INVALID(ofs)			\
573015516c77SSepherosa Ziehau 	((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN ||	\
573115516c77SSepherosa Ziehau 	 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
573215516c77SSepherosa Ziehau 
573315516c77SSepherosa Ziehau 	/* XXX Hyper-V does not meet data offset alignment requirement */
573415516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
573515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
573615516c77SSepherosa Ziehau 		    "data offset %u\n", pkt->rm_dataoffset);
573715516c77SSepherosa Ziehau 		return;
573815516c77SSepherosa Ziehau 	}
573915516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdataoffset > 0 &&
574015516c77SSepherosa Ziehau 	    IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
574115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
574215516c77SSepherosa Ziehau 		    "oob offset %u\n", pkt->rm_oobdataoffset);
574315516c77SSepherosa Ziehau 		return;
574415516c77SSepherosa Ziehau 	}
574515516c77SSepherosa Ziehau 	if (__predict_true(pkt->rm_pktinfooffset > 0) &&
574615516c77SSepherosa Ziehau 	    __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
574715516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
574815516c77SSepherosa Ziehau 		    "pktinfo offset %u\n", pkt->rm_pktinfooffset);
574915516c77SSepherosa Ziehau 		return;
575015516c77SSepherosa Ziehau 	}
575115516c77SSepherosa Ziehau 
575215516c77SSepherosa Ziehau #undef IS_OFFSET_INVALID
575315516c77SSepherosa Ziehau 
575415516c77SSepherosa Ziehau 	data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
575515516c77SSepherosa Ziehau 	data_len = pkt->rm_datalen;
575615516c77SSepherosa Ziehau 	pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
575715516c77SSepherosa Ziehau 	pktinfo_len = pkt->rm_pktinfolen;
575815516c77SSepherosa Ziehau 
575915516c77SSepherosa Ziehau 	/*
576015516c77SSepherosa Ziehau 	 * Check OOB coverage.
576115516c77SSepherosa Ziehau 	 */
576215516c77SSepherosa Ziehau 	if (__predict_false(pkt->rm_oobdatalen != 0)) {
576315516c77SSepherosa Ziehau 		int oob_off, oob_len;
576415516c77SSepherosa Ziehau 
576515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "got oobdata\n");
576615516c77SSepherosa Ziehau 		oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
576715516c77SSepherosa Ziehau 		oob_len = pkt->rm_oobdatalen;
576815516c77SSepherosa Ziehau 
576915516c77SSepherosa Ziehau 		if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
577015516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
577115516c77SSepherosa Ziehau 			    "oob overflow, msglen %u, oob abs %d len %d\n",
577215516c77SSepherosa Ziehau 			    pkt->rm_len, oob_off, oob_len);
577315516c77SSepherosa Ziehau 			return;
577415516c77SSepherosa Ziehau 		}
577515516c77SSepherosa Ziehau 
577615516c77SSepherosa Ziehau 		/*
577715516c77SSepherosa Ziehau 		 * Check against data.
577815516c77SSepherosa Ziehau 		 */
577915516c77SSepherosa Ziehau 		if (hn_rndis_check_overlap(oob_off, oob_len,
578015516c77SSepherosa Ziehau 		    data_off, data_len)) {
578115516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
578215516c77SSepherosa Ziehau 			    "oob overlaps data, oob abs %d len %d, "
578315516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
578415516c77SSepherosa Ziehau 			    oob_off, oob_len, data_off, data_len);
578515516c77SSepherosa Ziehau 			return;
578615516c77SSepherosa Ziehau 		}
578715516c77SSepherosa Ziehau 
578815516c77SSepherosa Ziehau 		/*
578915516c77SSepherosa Ziehau 		 * Check against pktinfo.
579015516c77SSepherosa Ziehau 		 */
579115516c77SSepherosa Ziehau 		if (pktinfo_len != 0 &&
579215516c77SSepherosa Ziehau 		    hn_rndis_check_overlap(oob_off, oob_len,
579315516c77SSepherosa Ziehau 		    pktinfo_off, pktinfo_len)) {
579415516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
579515516c77SSepherosa Ziehau 			    "oob overlaps pktinfo, oob abs %d len %d, "
579615516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
579715516c77SSepherosa Ziehau 			    oob_off, oob_len, pktinfo_off, pktinfo_len);
579815516c77SSepherosa Ziehau 			return;
579915516c77SSepherosa Ziehau 		}
580015516c77SSepherosa Ziehau 	}
580115516c77SSepherosa Ziehau 
580215516c77SSepherosa Ziehau 	/*
580315516c77SSepherosa Ziehau 	 * Check per-packet-info coverage and find useful per-packet-info.
580415516c77SSepherosa Ziehau 	 */
580515516c77SSepherosa Ziehau 	info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
580615516c77SSepherosa Ziehau 	info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
580715516c77SSepherosa Ziehau 	info.hash_info = HN_NDIS_HASH_INFO_INVALID;
580815516c77SSepherosa Ziehau 	if (__predict_true(pktinfo_len != 0)) {
580915516c77SSepherosa Ziehau 		bool overlap;
581015516c77SSepherosa Ziehau 		int error;
581115516c77SSepherosa Ziehau 
581215516c77SSepherosa Ziehau 		if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
581315516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
581415516c77SSepherosa Ziehau 			    "pktinfo overflow, msglen %u, "
581515516c77SSepherosa Ziehau 			    "pktinfo abs %d len %d\n",
581615516c77SSepherosa Ziehau 			    pkt->rm_len, pktinfo_off, pktinfo_len);
581715516c77SSepherosa Ziehau 			return;
581815516c77SSepherosa Ziehau 		}
581915516c77SSepherosa Ziehau 
582015516c77SSepherosa Ziehau 		/*
582115516c77SSepherosa Ziehau 		 * Check packet info coverage.
582215516c77SSepherosa Ziehau 		 */
582315516c77SSepherosa Ziehau 		overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
582415516c77SSepherosa Ziehau 		    data_off, data_len);
582515516c77SSepherosa Ziehau 		if (__predict_false(overlap)) {
582615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
582715516c77SSepherosa Ziehau 			    "pktinfo overlap data, pktinfo abs %d len %d, "
582815516c77SSepherosa Ziehau 			    "data abs %d len %d\n",
582915516c77SSepherosa Ziehau 			    pktinfo_off, pktinfo_len, data_off, data_len);
583015516c77SSepherosa Ziehau 			return;
583115516c77SSepherosa Ziehau 		}
583215516c77SSepherosa Ziehau 
583315516c77SSepherosa Ziehau 		/*
583415516c77SSepherosa Ziehau 		 * Find useful per-packet-info.
583515516c77SSepherosa Ziehau 		 */
583615516c77SSepherosa Ziehau 		error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
583715516c77SSepherosa Ziehau 		    pktinfo_len, &info);
583815516c77SSepherosa Ziehau 		if (__predict_false(error)) {
583915516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
584015516c77SSepherosa Ziehau 			    "pktinfo\n");
584115516c77SSepherosa Ziehau 			return;
584215516c77SSepherosa Ziehau 		}
584315516c77SSepherosa Ziehau 	}
584415516c77SSepherosa Ziehau 
584515516c77SSepherosa Ziehau 	if (__predict_false(data_off + data_len > pkt->rm_len)) {
584615516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
584715516c77SSepherosa Ziehau 		    "data overflow, msglen %u, data abs %d len %d\n",
584815516c77SSepherosa Ziehau 		    pkt->rm_len, data_off, data_len);
584915516c77SSepherosa Ziehau 		return;
585015516c77SSepherosa Ziehau 	}
585115516c77SSepherosa Ziehau 	hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
585215516c77SSepherosa Ziehau }
585315516c77SSepherosa Ziehau 
585415516c77SSepherosa Ziehau static __inline void
585515516c77SSepherosa Ziehau hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen)
585615516c77SSepherosa Ziehau {
585715516c77SSepherosa Ziehau 	const struct rndis_msghdr *hdr;
585815516c77SSepherosa Ziehau 
585915516c77SSepherosa Ziehau 	if (__predict_false(dlen < sizeof(*hdr))) {
586015516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
586115516c77SSepherosa Ziehau 		return;
586215516c77SSepherosa Ziehau 	}
586315516c77SSepherosa Ziehau 	hdr = data;
586415516c77SSepherosa Ziehau 
586515516c77SSepherosa Ziehau 	if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) {
586615516c77SSepherosa Ziehau 		/* Hot data path. */
586715516c77SSepherosa Ziehau 		hn_rndis_rx_data(rxr, data, dlen);
586815516c77SSepherosa Ziehau 		/* Done! */
586915516c77SSepherosa Ziehau 		return;
587015516c77SSepherosa Ziehau 	}
587115516c77SSepherosa Ziehau 
587215516c77SSepherosa Ziehau 	if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG)
587315516c77SSepherosa Ziehau 		hn_rndis_rx_status(rxr->hn_ifp->if_softc, data, dlen);
587415516c77SSepherosa Ziehau 	else
587515516c77SSepherosa Ziehau 		hn_rndis_rx_ctrl(rxr->hn_ifp->if_softc, data, dlen);
587615516c77SSepherosa Ziehau }
587715516c77SSepherosa Ziehau 
587815516c77SSepherosa Ziehau static void
587915516c77SSepherosa Ziehau hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
588015516c77SSepherosa Ziehau {
588115516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *hdr;
588215516c77SSepherosa Ziehau 
588315516c77SSepherosa Ziehau 	if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) {
588415516c77SSepherosa Ziehau 		if_printf(sc->hn_ifp, "invalid nvs notify\n");
588515516c77SSepherosa Ziehau 		return;
588615516c77SSepherosa Ziehau 	}
588715516c77SSepherosa Ziehau 	hdr = VMBUS_CHANPKT_CONST_DATA(pkt);
588815516c77SSepherosa Ziehau 
588915516c77SSepherosa Ziehau 	if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) {
589015516c77SSepherosa Ziehau 		/* Useless; ignore */
589115516c77SSepherosa Ziehau 		return;
589215516c77SSepherosa Ziehau 	}
589315516c77SSepherosa Ziehau 	if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type);
589415516c77SSepherosa Ziehau }
589515516c77SSepherosa Ziehau 
589615516c77SSepherosa Ziehau static void
589715516c77SSepherosa Ziehau hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
589815516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkt)
589915516c77SSepherosa Ziehau {
590015516c77SSepherosa Ziehau 	struct hn_nvs_sendctx *sndc;
590115516c77SSepherosa Ziehau 
590215516c77SSepherosa Ziehau 	sndc = (struct hn_nvs_sendctx *)(uintptr_t)pkt->cph_xactid;
590315516c77SSepherosa Ziehau 	sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt),
590415516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_DATALEN(pkt));
590515516c77SSepherosa Ziehau 	/*
590615516c77SSepherosa Ziehau 	 * NOTE:
590715516c77SSepherosa Ziehau 	 * 'sndc' CAN NOT be accessed anymore, since it can be freed by
590815516c77SSepherosa Ziehau 	 * its callback.
590915516c77SSepherosa Ziehau 	 */
591015516c77SSepherosa Ziehau }
591115516c77SSepherosa Ziehau 
591215516c77SSepherosa Ziehau static void
591315516c77SSepherosa Ziehau hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
591415516c77SSepherosa Ziehau     const struct vmbus_chanpkt_hdr *pkthdr)
591515516c77SSepherosa Ziehau {
591615516c77SSepherosa Ziehau 	const struct vmbus_chanpkt_rxbuf *pkt;
591715516c77SSepherosa Ziehau 	const struct hn_nvs_hdr *nvs_hdr;
591815516c77SSepherosa Ziehau 	int count, i, hlen;
591915516c77SSepherosa Ziehau 
592015516c77SSepherosa Ziehau 	if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) {
592115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n");
592215516c77SSepherosa Ziehau 		return;
592315516c77SSepherosa Ziehau 	}
592415516c77SSepherosa Ziehau 	nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr);
592515516c77SSepherosa Ziehau 
592615516c77SSepherosa Ziehau 	/* Make sure that this is a RNDIS message. */
592715516c77SSepherosa Ziehau 	if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) {
592815516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n",
592915516c77SSepherosa Ziehau 		    nvs_hdr->nvs_type);
593015516c77SSepherosa Ziehau 		return;
593115516c77SSepherosa Ziehau 	}
593215516c77SSepherosa Ziehau 
593315516c77SSepherosa Ziehau 	hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen);
593415516c77SSepherosa Ziehau 	if (__predict_false(hlen < sizeof(*pkt))) {
593515516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n");
593615516c77SSepherosa Ziehau 		return;
593715516c77SSepherosa Ziehau 	}
593815516c77SSepherosa Ziehau 	pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr;
593915516c77SSepherosa Ziehau 
594015516c77SSepherosa Ziehau 	if (__predict_false(pkt->cp_rxbuf_id != HN_NVS_RXBUF_SIG)) {
594115516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n",
594215516c77SSepherosa Ziehau 		    pkt->cp_rxbuf_id);
594315516c77SSepherosa Ziehau 		return;
594415516c77SSepherosa Ziehau 	}
594515516c77SSepherosa Ziehau 
594615516c77SSepherosa Ziehau 	count = pkt->cp_rxbuf_cnt;
594715516c77SSepherosa Ziehau 	if (__predict_false(hlen <
594815516c77SSepherosa Ziehau 	    __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) {
594915516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count);
595015516c77SSepherosa Ziehau 		return;
595115516c77SSepherosa Ziehau 	}
595215516c77SSepherosa Ziehau 
595315516c77SSepherosa Ziehau 	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
595415516c77SSepherosa Ziehau 	for (i = 0; i < count; ++i) {
595515516c77SSepherosa Ziehau 		int ofs, len;
595615516c77SSepherosa Ziehau 
595715516c77SSepherosa Ziehau 		ofs = pkt->cp_rxbuf[i].rb_ofs;
595815516c77SSepherosa Ziehau 		len = pkt->cp_rxbuf[i].rb_len;
595915516c77SSepherosa Ziehau 		if (__predict_false(ofs + len > HN_RXBUF_SIZE)) {
596015516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, "
596115516c77SSepherosa Ziehau 			    "ofs %d, len %d\n", i, ofs, len);
596215516c77SSepherosa Ziehau 			continue;
596315516c77SSepherosa Ziehau 		}
596415516c77SSepherosa Ziehau 		hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len);
596515516c77SSepherosa Ziehau 	}
596615516c77SSepherosa Ziehau 
596715516c77SSepherosa Ziehau 	/*
596815516c77SSepherosa Ziehau 	 * Ack the consumed RXBUF associated w/ this channel packet,
596915516c77SSepherosa Ziehau 	 * so that this RXBUF can be recycled by the hypervisor.
597015516c77SSepherosa Ziehau 	 */
597115516c77SSepherosa Ziehau 	hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid);
597215516c77SSepherosa Ziehau }
597315516c77SSepherosa Ziehau 
597415516c77SSepherosa Ziehau static void
597515516c77SSepherosa Ziehau hn_nvs_ack_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan,
597615516c77SSepherosa Ziehau     uint64_t tid)
597715516c77SSepherosa Ziehau {
597815516c77SSepherosa Ziehau 	struct hn_nvs_rndis_ack ack;
597915516c77SSepherosa Ziehau 	int retries, error;
598015516c77SSepherosa Ziehau 
598115516c77SSepherosa Ziehau 	ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK;
598215516c77SSepherosa Ziehau 	ack.nvs_status = HN_NVS_STATUS_OK;
598315516c77SSepherosa Ziehau 
598415516c77SSepherosa Ziehau 	retries = 0;
598515516c77SSepherosa Ziehau again:
598615516c77SSepherosa Ziehau 	error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
598715516c77SSepherosa Ziehau 	    VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid);
598815516c77SSepherosa Ziehau 	if (__predict_false(error == EAGAIN)) {
598915516c77SSepherosa Ziehau 		/*
599015516c77SSepherosa Ziehau 		 * NOTE:
599115516c77SSepherosa Ziehau 		 * This should _not_ happen in real world, since the
599215516c77SSepherosa Ziehau 		 * consumption of the TX bufring from the TX path is
599315516c77SSepherosa Ziehau 		 * controlled.
599415516c77SSepherosa Ziehau 		 */
599515516c77SSepherosa Ziehau 		if (rxr->hn_ack_failed == 0)
599615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "RXBUF ack retry\n");
599715516c77SSepherosa Ziehau 		rxr->hn_ack_failed++;
599815516c77SSepherosa Ziehau 		retries++;
599915516c77SSepherosa Ziehau 		if (retries < 10) {
600015516c77SSepherosa Ziehau 			DELAY(100);
600115516c77SSepherosa Ziehau 			goto again;
600215516c77SSepherosa Ziehau 		}
600315516c77SSepherosa Ziehau 		/* RXBUF leaks! */
600415516c77SSepherosa Ziehau 		if_printf(rxr->hn_ifp, "RXBUF ack failed\n");
600515516c77SSepherosa Ziehau 	}
600615516c77SSepherosa Ziehau }
600715516c77SSepherosa Ziehau 
600815516c77SSepherosa Ziehau static void
600915516c77SSepherosa Ziehau hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
601015516c77SSepherosa Ziehau {
601115516c77SSepherosa Ziehau 	struct hn_rx_ring *rxr = xrxr;
601215516c77SSepherosa Ziehau 	struct hn_softc *sc = rxr->hn_ifp->if_softc;
601315516c77SSepherosa Ziehau 
601415516c77SSepherosa Ziehau 	for (;;) {
601515516c77SSepherosa Ziehau 		struct vmbus_chanpkt_hdr *pkt = rxr->hn_pktbuf;
601615516c77SSepherosa Ziehau 		int error, pktlen;
601715516c77SSepherosa Ziehau 
601815516c77SSepherosa Ziehau 		pktlen = rxr->hn_pktbuf_len;
601915516c77SSepherosa Ziehau 		error = vmbus_chan_recv_pkt(chan, pkt, &pktlen);
602015516c77SSepherosa Ziehau 		if (__predict_false(error == ENOBUFS)) {
602115516c77SSepherosa Ziehau 			void *nbuf;
602215516c77SSepherosa Ziehau 			int nlen;
602315516c77SSepherosa Ziehau 
602415516c77SSepherosa Ziehau 			/*
602515516c77SSepherosa Ziehau 			 * Expand channel packet buffer.
602615516c77SSepherosa Ziehau 			 *
602715516c77SSepherosa Ziehau 			 * XXX
602815516c77SSepherosa Ziehau 			 * Use M_WAITOK here, since allocation failure
602915516c77SSepherosa Ziehau 			 * is fatal.
603015516c77SSepherosa Ziehau 			 */
603115516c77SSepherosa Ziehau 			nlen = rxr->hn_pktbuf_len * 2;
603215516c77SSepherosa Ziehau 			while (nlen < pktlen)
603315516c77SSepherosa Ziehau 				nlen *= 2;
603415516c77SSepherosa Ziehau 			nbuf = malloc(nlen, M_DEVBUF, M_WAITOK);
603515516c77SSepherosa Ziehau 
603615516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "expand pktbuf %d -> %d\n",
603715516c77SSepherosa Ziehau 			    rxr->hn_pktbuf_len, nlen);
603815516c77SSepherosa Ziehau 
603915516c77SSepherosa Ziehau 			free(rxr->hn_pktbuf, M_DEVBUF);
604015516c77SSepherosa Ziehau 			rxr->hn_pktbuf = nbuf;
604115516c77SSepherosa Ziehau 			rxr->hn_pktbuf_len = nlen;
604215516c77SSepherosa Ziehau 			/* Retry! */
604315516c77SSepherosa Ziehau 			continue;
604415516c77SSepherosa Ziehau 		} else if (__predict_false(error == EAGAIN)) {
604515516c77SSepherosa Ziehau 			/* No more channel packets; done! */
604615516c77SSepherosa Ziehau 			break;
604715516c77SSepherosa Ziehau 		}
604815516c77SSepherosa Ziehau 		KASSERT(!error, ("vmbus_chan_recv_pkt failed: %d", error));
604915516c77SSepherosa Ziehau 
605015516c77SSepherosa Ziehau 		switch (pkt->cph_type) {
605115516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_COMP:
605215516c77SSepherosa Ziehau 			hn_nvs_handle_comp(sc, chan, pkt);
605315516c77SSepherosa Ziehau 			break;
605415516c77SSepherosa Ziehau 
605515516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_RXBUF:
605615516c77SSepherosa Ziehau 			hn_nvs_handle_rxbuf(rxr, chan, pkt);
605715516c77SSepherosa Ziehau 			break;
605815516c77SSepherosa Ziehau 
605915516c77SSepherosa Ziehau 		case VMBUS_CHANPKT_TYPE_INBAND:
606015516c77SSepherosa Ziehau 			hn_nvs_handle_notify(sc, pkt);
606115516c77SSepherosa Ziehau 			break;
606215516c77SSepherosa Ziehau 
606315516c77SSepherosa Ziehau 		default:
606415516c77SSepherosa Ziehau 			if_printf(rxr->hn_ifp, "unknown chan pkt %u\n",
606515516c77SSepherosa Ziehau 			    pkt->cph_type);
606615516c77SSepherosa Ziehau 			break;
606715516c77SSepherosa Ziehau 		}
606815516c77SSepherosa Ziehau 	}
606915516c77SSepherosa Ziehau 	hn_chan_rollup(rxr, rxr->hn_txr);
607015516c77SSepherosa Ziehau }
607115516c77SSepherosa Ziehau 
607215516c77SSepherosa Ziehau static void
6073499c3e17SSepherosa Ziehau hn_sysinit(void *arg __unused)
607415516c77SSepherosa Ziehau {
6075fdd0222aSSepherosa Ziehau 	int i;
6076fdd0222aSSepherosa Ziehau 
6077fdd0222aSSepherosa Ziehau 	/*
6078499c3e17SSepherosa Ziehau 	 * Initialize VF map.
6079499c3e17SSepherosa Ziehau 	 */
6080499c3e17SSepherosa Ziehau 	rm_init_flags(&hn_vfmap_lock, "hn_vfmap", RM_SLEEPABLE);
6081499c3e17SSepherosa Ziehau 	hn_vfmap_size = HN_VFMAP_SIZE_DEF;
6082499c3e17SSepherosa Ziehau 	hn_vfmap = malloc(sizeof(struct ifnet *) * hn_vfmap_size, M_DEVBUF,
6083499c3e17SSepherosa Ziehau 	    M_WAITOK | M_ZERO);
6084499c3e17SSepherosa Ziehau 
6085499c3e17SSepherosa Ziehau 	/*
6086fdd0222aSSepherosa Ziehau 	 * Fix the # of TX taskqueues.
6087fdd0222aSSepherosa Ziehau 	 */
6088fdd0222aSSepherosa Ziehau 	if (hn_tx_taskq_cnt <= 0)
6089fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = 1;
6090fdd0222aSSepherosa Ziehau 	else if (hn_tx_taskq_cnt > mp_ncpus)
6091fdd0222aSSepherosa Ziehau 		hn_tx_taskq_cnt = mp_ncpus;
609215516c77SSepherosa Ziehau 
60930e11868dSSepherosa Ziehau 	/*
60940e11868dSSepherosa Ziehau 	 * Fix the TX taskqueue mode.
60950e11868dSSepherosa Ziehau 	 */
60960e11868dSSepherosa Ziehau 	switch (hn_tx_taskq_mode) {
60970e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_INDEP:
60980e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_GLOBAL:
60990e11868dSSepherosa Ziehau 	case HN_TX_TASKQ_M_EVTTQ:
61000e11868dSSepherosa Ziehau 		break;
61010e11868dSSepherosa Ziehau 	default:
61020e11868dSSepherosa Ziehau 		hn_tx_taskq_mode = HN_TX_TASKQ_M_INDEP;
61030e11868dSSepherosa Ziehau 		break;
61040e11868dSSepherosa Ziehau 	}
61050e11868dSSepherosa Ziehau 
610615516c77SSepherosa Ziehau 	if (vm_guest != VM_GUEST_HV)
610715516c77SSepherosa Ziehau 		return;
610815516c77SSepherosa Ziehau 
61090e11868dSSepherosa Ziehau 	if (hn_tx_taskq_mode != HN_TX_TASKQ_M_GLOBAL)
611015516c77SSepherosa Ziehau 		return;
611115516c77SSepherosa Ziehau 
6112fdd0222aSSepherosa Ziehau 	hn_tx_taskque = malloc(hn_tx_taskq_cnt * sizeof(struct taskqueue *),
6113fdd0222aSSepherosa Ziehau 	    M_DEVBUF, M_WAITOK);
6114fdd0222aSSepherosa Ziehau 	for (i = 0; i < hn_tx_taskq_cnt; ++i) {
6115fdd0222aSSepherosa Ziehau 		hn_tx_taskque[i] = taskqueue_create("hn_tx", M_WAITOK,
6116fdd0222aSSepherosa Ziehau 		    taskqueue_thread_enqueue, &hn_tx_taskque[i]);
6117fdd0222aSSepherosa Ziehau 		taskqueue_start_threads(&hn_tx_taskque[i], 1, PI_NET,
6118fdd0222aSSepherosa Ziehau 		    "hn tx%d", i);
6119fdd0222aSSepherosa Ziehau 	}
612015516c77SSepherosa Ziehau }
6121499c3e17SSepherosa Ziehau SYSINIT(hn_sysinit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysinit, NULL);
612215516c77SSepherosa Ziehau 
612315516c77SSepherosa Ziehau static void
6124499c3e17SSepherosa Ziehau hn_sysuninit(void *arg __unused)
612515516c77SSepherosa Ziehau {
612615516c77SSepherosa Ziehau 
6127fdd0222aSSepherosa Ziehau 	if (hn_tx_taskque != NULL) {
6128fdd0222aSSepherosa Ziehau 		int i;
6129fdd0222aSSepherosa Ziehau 
6130fdd0222aSSepherosa Ziehau 		for (i = 0; i < hn_tx_taskq_cnt; ++i)
6131fdd0222aSSepherosa Ziehau 			taskqueue_free(hn_tx_taskque[i]);
6132fdd0222aSSepherosa Ziehau 		free(hn_tx_taskque, M_DEVBUF);
6133fdd0222aSSepherosa Ziehau 	}
6134499c3e17SSepherosa Ziehau 
6135499c3e17SSepherosa Ziehau 	if (hn_vfmap != NULL)
6136499c3e17SSepherosa Ziehau 		free(hn_vfmap, M_DEVBUF);
6137499c3e17SSepherosa Ziehau 	rm_destroy(&hn_vfmap_lock);
613815516c77SSepherosa Ziehau }
6139499c3e17SSepherosa Ziehau SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL);
6140