summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorRenato Botelho <renato@netgate.com>2016-02-24 07:51:32 -0300
committerRenato Botelho <renato@netgate.com>2016-02-24 07:51:32 -0300
commitb15d3cfa0625b6816b5b55df864fbda78dc2add8 (patch)
treec384e7235e9894678587ee5782698526bdcc340e /sys/dev
parent7c17fc70241a215de420457e10a510834441b90f (diff)
parent008df39fd8f9ba2311709c852fa30e39bf891bcf (diff)
downloadFreeBSD-src-b15d3cfa0625b6816b5b55df864fbda78dc2add8.zip
FreeBSD-src-b15d3cfa0625b6816b5b55df864fbda78dc2add8.tar.gz
Merge remote-tracking branch 'origin/stable/10' into devel
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.c2
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.h26
-rw-r--r--sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c663
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis.h1
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis_filter.c18
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis_filter.h1
-rw-r--r--sys/dev/hyperv/vmbus/hv_channel_mgmt.c21
-rw-r--r--sys/dev/hyperv/vmbus/hv_connection.c38
-rw-r--r--sys/dev/hyperv/vmbus/hv_vmbus_priv.h11
9 files changed, 593 insertions, 188 deletions
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.c b/sys/dev/hyperv/netvsc/hv_net_vsc.c
index a44c30d..64e7578 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.c
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.c
@@ -1027,4 +1027,6 @@ hv_nv_on_channel_callback(void *context)
if (bufferlen > NETVSC_PACKET_SIZE)
free(buffer, M_NETVSC);
+
+ hv_rf_channel_rollup(net_dev);
}
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h
index 4e63b94..e684cc5 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.h
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h
@@ -38,12 +38,16 @@
#ifndef __HV_NET_VSC_H__
#define __HV_NET_VSC_H__
-#include <sys/types.h>
#include <sys/param.h>
#include <sys/lock.h>
#include <sys/malloc.h>
+#include <sys/queue.h>
#include <sys/sx.h>
+#include <machine/bus.h>
+#include <sys/bus.h>
+#include <sys/bus_dma.h>
+
#include <netinet/in.h>
#include <netinet/tcp_lro.h>
@@ -984,6 +988,9 @@ typedef struct {
hv_bool_uint8_t link_state;
} netvsc_device_info;
+struct hn_txdesc;
+SLIST_HEAD(hn_txdesc_list, hn_txdesc);
+
/*
* Device-specific softc structure
*/
@@ -1002,6 +1009,18 @@ typedef struct hn_softc {
struct hv_device *hn_dev_obj;
netvsc_dev *net_dev;
+ int hn_txdesc_cnt;
+ struct hn_txdesc *hn_txdesc;
+ bus_dma_tag_t hn_tx_data_dtag;
+ bus_dma_tag_t hn_tx_rndis_dtag;
+ int hn_tx_chimney_size;
+ int hn_tx_chimney_max;
+
+ struct mtx hn_txlist_spin;
+ struct hn_txdesc_list hn_txlist;
+ int hn_txdesc_avail;
+ int hn_txeof;
+
struct lro_ctrl hn_lro;
int hn_lro_hiwat;
@@ -1013,6 +1032,11 @@ typedef struct hn_softc {
u_long hn_csum_trusted;
u_long hn_lro_tried;
u_long hn_small_pkts;
+ u_long hn_no_txdescs;
+ u_long hn_send_failed;
+ u_long hn_txdma_failed;
+ u_long hn_tx_collapsed;
+ u_long hn_tx_chimney;
} hn_softc_t;
diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
index f8ebd38..b3360ea 100644
--- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
+++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
@@ -129,6 +129,41 @@ __FBSDID("$FreeBSD$");
#define HV_NV_SC_PTR_OFFSET_IN_BUF 0
#define HV_NV_PACKET_OFFSET_IN_BUF 16
+/* YYY should get it from the underlying channel */
+#define HN_TX_DESC_CNT 512
+
+#define HN_RNDIS_MSG_LEN \
+ (sizeof(rndis_msg) + \
+ RNDIS_VLAN_PPI_SIZE + \
+ RNDIS_TSO_PPI_SIZE + \
+ RNDIS_CSUM_PPI_SIZE)
+#define HN_RNDIS_MSG_BOUNDARY PAGE_SIZE
+#define HN_RNDIS_MSG_ALIGN CACHE_LINE_SIZE
+
+#define HN_TX_DATA_BOUNDARY PAGE_SIZE
+#define HN_TX_DATA_MAXSIZE IP_MAXPACKET
+#define HN_TX_DATA_SEGSIZE PAGE_SIZE
+#define HN_TX_DATA_SEGCNT_MAX \
+ (NETVSC_PACKET_MAXPAGE - HV_RF_NUM_TX_RESERVED_PAGE_BUFS)
+
+struct hn_txdesc {
+ SLIST_ENTRY(hn_txdesc) link;
+ struct mbuf *m;
+ struct hn_softc *sc;
+ int refs;
+ uint32_t flags; /* HN_TXD_FLAG_ */
+ netvsc_packet netvsc_pkt; /* XXX to be removed */
+
+ bus_dmamap_t data_dmap;
+
+ bus_addr_t rndis_msg_paddr;
+ rndis_msg *rndis_msg;
+ bus_dmamap_t rndis_msg_dmap;
+};
+
+#define HN_TXD_FLAG_ONLIST 0x1
+#define HN_TXD_FLAG_DMAMAP 0x2
+
/*
* A unified flag for all outbound check sum flags is useful,
* and it helps avoiding unnecessary check sum calculation in
@@ -174,6 +209,16 @@ int hv_promisc_mode = 0; /* normal mode by default */
static int hn_trust_hosttcp = 0;
TUNABLE_INT("dev.hn.trust_hosttcp", &hn_trust_hosttcp);
+#if __FreeBSD_version >= 1100045
+/* Limit TSO burst size */
+static int hn_tso_maxlen = 0;
+TUNABLE_INT("dev.hn.tso_maxlen", &hn_tso_maxlen);
+#endif
+
+/* Limit chimney send size */
+static int hn_tx_chimney_size = 0;
+TUNABLE_INT("dev.hn.tx_chimney_size", &hn_tx_chimney_size);
+
/*
* Forward declarations
*/
@@ -181,14 +226,17 @@ static void hn_stop(hn_softc_t *sc);
static void hn_ifinit_locked(hn_softc_t *sc);
static void hn_ifinit(void *xsc);
static int hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
-static int hn_start_locked(struct ifnet *ifp);
+static void hn_start_locked(struct ifnet *ifp);
static void hn_start(struct ifnet *ifp);
static int hn_ifmedia_upd(struct ifnet *ifp);
static void hn_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
#ifdef HN_LRO_HIWAT
static int hn_lro_hiwat_sysctl(SYSCTL_HANDLER_ARGS);
#endif
+static int hn_tx_chimney_size_sysctl(SYSCTL_HANDLER_ARGS);
static int hn_check_iplen(const struct mbuf *, int);
+static int hn_create_tx_ring(struct hn_softc *sc);
+static void hn_destroy_tx_ring(struct hn_softc *sc);
static __inline void
hn_set_lro_hiwat(struct hn_softc *sc, int hiwat)
@@ -318,10 +366,13 @@ netvsc_attach(device_t dev)
netvsc_device_info device_info;
hn_softc_t *sc;
int unit = device_get_unit(dev);
- struct ifnet *ifp;
+ struct ifnet *ifp = NULL;
struct sysctl_oid_list *child;
struct sysctl_ctx_list *ctx;
- int ret;
+ int error;
+#if __FreeBSD_version >= 1100045
+ int tso_maxlen;
+#endif
sc = device_get_softc(dev);
if (sc == NULL) {
@@ -334,6 +385,10 @@ netvsc_attach(device_t dev)
sc->hn_lro_hiwat = HN_LRO_HIWAT_DEF;
sc->hn_trust_hosttcp = hn_trust_hosttcp;
+ error = hn_create_tx_ring(sc);
+ if (error)
+ goto failed;
+
NV_LOCK_INIT(sc, "NetVSCLock");
sc->hn_dev_obj = device_ctx;
@@ -381,12 +436,10 @@ netvsc_attach(device_t dev)
else
ifp->if_hwassist = CSUM_TCP | CSUM_TSO;
- ret = hv_rf_on_device_add(device_ctx, &device_info);
- if (ret != 0) {
- if_free(ifp);
+ error = hv_rf_on_device_add(device_ctx, &device_info);
+ if (error)
+ goto failed;
- return (ret);
- }
if (device_info.link_state == 0) {
sc->hn_carrier = 1;
}
@@ -400,8 +453,30 @@ netvsc_attach(device_t dev)
#endif
#endif /* INET || INET6 */
+#if __FreeBSD_version >= 1100045
+ tso_maxlen = hn_tso_maxlen;
+ if (tso_maxlen <= 0 || tso_maxlen > IP_MAXPACKET)
+ tso_maxlen = IP_MAXPACKET;
+
+ ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX;
+ ifp->if_hw_tsomaxsegsize = PAGE_SIZE;
+ ifp->if_hw_tsomax = tso_maxlen -
+ (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
+#endif
+
ether_ifattach(ifp, device_info.mac_addr);
+#if __FreeBSD_version >= 1100045
+ if_printf(ifp, "TSO: %u/%u/%u\n", ifp->if_hw_tsomax,
+ ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize);
+#endif
+
+ sc->hn_tx_chimney_max = sc->net_dev->send_section_size;
+ sc->hn_tx_chimney_size = sc->hn_tx_chimney_max;
+ if (hn_tx_chimney_size > 0 &&
+ hn_tx_chimney_size < sc->hn_tx_chimney_max)
+ sc->hn_tx_chimney_size = hn_tx_chimney_size;
+
ctx = device_get_sysctl_ctx(dev);
child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
@@ -429,6 +504,26 @@ netvsc_attach(device_t dev)
"# of TCP segements that we trust host's csum verification");
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "small_pkts",
CTLFLAG_RW, &sc->hn_small_pkts, "# of small packets received");
+ SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "no_txdescs",
+ CTLFLAG_RW, &sc->hn_no_txdescs, "# of times short of TX descs");
+ SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "send_failed",
+ CTLFLAG_RW, &sc->hn_send_failed, "# of hyper-v sending failure");
+ SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "txdma_failed",
+ CTLFLAG_RW, &sc->hn_txdma_failed, "# of TX DMA failure");
+ SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_collapsed",
+ CTLFLAG_RW, &sc->hn_tx_collapsed, "# of TX mbuf collapsed");
+ SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_chimney",
+ CTLFLAG_RW, &sc->hn_tx_chimney, "# of chimney send");
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_cnt",
+ CTLFLAG_RD, &sc->hn_txdesc_cnt, 0, "# of total TX descs");
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "txdesc_avail",
+ CTLFLAG_RD, &sc->hn_txdesc_avail, 0, "# of available TX descs");
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "tx_chimney_max",
+ CTLFLAG_RD, &sc->hn_tx_chimney_max, 0,
+ "Chimney send packet size upper boundary");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "tx_chimney_size",
+ CTLTYPE_INT | CTLFLAG_RW, sc, 0, hn_tx_chimney_size_sysctl,
+ "I", "Chimney send packet size limit");
if (unit == 0) {
struct sysctl_ctx_list *dc_ctx;
@@ -446,9 +541,21 @@ netvsc_attach(device_t dev)
CTLFLAG_RD, &hn_trust_hosttcp, 0,
"Trust tcp segement verification on host side, "
"when csum info is missing (global setting)");
+ SYSCTL_ADD_INT(dc_ctx, dc_child, OID_AUTO, "tx_chimney_size",
+ CTLFLAG_RD, &hn_tx_chimney_size, 0,
+ "Chimney send packet size limit");
+#if __FreeBSD_version >= 1100045
+ SYSCTL_ADD_INT(dc_ctx, dc_child, OID_AUTO, "tso_maxlen",
+ CTLFLAG_RD, &hn_tso_maxlen, 0, "TSO burst limit");
+#endif
}
return (0);
+failed:
+ hn_destroy_tx_ring(sc);
+ if (ifp != NULL)
+ if_free(ifp);
+ return (error);
}
/*
@@ -480,6 +587,7 @@ netvsc_detach(device_t dev)
#if defined(INET) || defined(INET6)
tcp_lro_free(&sc->hn_lro);
#endif
+ hn_destroy_tx_ring(sc);
return (0);
}
@@ -493,6 +601,112 @@ netvsc_shutdown(device_t dev)
return (0);
}
+static __inline int
+hn_txdesc_dmamap_load(struct hn_softc *sc, struct hn_txdesc *txd,
+ struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
+{
+ struct mbuf *m = *m_head;
+ int error;
+
+ error = bus_dmamap_load_mbuf_sg(sc->hn_tx_data_dtag, txd->data_dmap,
+ m, segs, nsegs, BUS_DMA_NOWAIT);
+ if (error == EFBIG) {
+ struct mbuf *m_new;
+
+ m_new = m_collapse(m, M_NOWAIT, HN_TX_DATA_SEGCNT_MAX);
+ if (m_new == NULL)
+ return ENOBUFS;
+ else
+ *m_head = m = m_new;
+ sc->hn_tx_collapsed++;
+
+ error = bus_dmamap_load_mbuf_sg(sc->hn_tx_data_dtag,
+ txd->data_dmap, m, segs, nsegs, BUS_DMA_NOWAIT);
+ }
+ if (!error) {
+ bus_dmamap_sync(sc->hn_tx_data_dtag, txd->data_dmap,
+ BUS_DMASYNC_PREWRITE);
+ txd->flags |= HN_TXD_FLAG_DMAMAP;
+ }
+ return error;
+}
+
+static __inline void
+hn_txdesc_dmamap_unload(struct hn_softc *sc, struct hn_txdesc *txd)
+{
+
+ if (txd->flags & HN_TXD_FLAG_DMAMAP) {
+ bus_dmamap_sync(sc->hn_tx_data_dtag,
+ txd->data_dmap, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->hn_tx_data_dtag,
+ txd->data_dmap);
+ txd->flags &= ~HN_TXD_FLAG_DMAMAP;
+ }
+}
+
+static __inline int
+hn_txdesc_put(struct hn_softc *sc, struct hn_txdesc *txd)
+{
+
+ KASSERT((txd->flags & HN_TXD_FLAG_ONLIST) == 0,
+ ("put an onlist txd %#x", txd->flags));
+
+ KASSERT(txd->refs > 0, ("invalid txd refs %d", txd->refs));
+ if (atomic_fetchadd_int(&txd->refs, -1) != 1)
+ return 0;
+
+ hn_txdesc_dmamap_unload(sc, txd);
+ if (txd->m != NULL) {
+ m_freem(txd->m);
+ txd->m = NULL;
+ }
+
+ txd->flags |= HN_TXD_FLAG_ONLIST;
+
+ mtx_lock_spin(&sc->hn_txlist_spin);
+ KASSERT(sc->hn_txdesc_avail >= 0 &&
+ sc->hn_txdesc_avail < sc->hn_txdesc_cnt,
+ ("txdesc_put: invalid txd avail %d", sc->hn_txdesc_avail));
+ sc->hn_txdesc_avail++;
+ SLIST_INSERT_HEAD(&sc->hn_txlist, txd, link);
+ mtx_unlock_spin(&sc->hn_txlist_spin);
+
+ return 1;
+}
+
+static __inline struct hn_txdesc *
+hn_txdesc_get(struct hn_softc *sc)
+{
+ struct hn_txdesc *txd;
+
+ mtx_lock_spin(&sc->hn_txlist_spin);
+ txd = SLIST_FIRST(&sc->hn_txlist);
+ if (txd != NULL) {
+ KASSERT(sc->hn_txdesc_avail > 0,
+ ("txdesc_get: invalid txd avail %d", sc->hn_txdesc_avail));
+ sc->hn_txdesc_avail--;
+ SLIST_REMOVE_HEAD(&sc->hn_txlist, link);
+ }
+ mtx_unlock_spin(&sc->hn_txlist_spin);
+
+ if (txd != NULL) {
+ KASSERT(txd->m == NULL && txd->refs == 0 &&
+ (txd->flags & HN_TXD_FLAG_ONLIST), ("invalid txd"));
+ txd->flags &= ~HN_TXD_FLAG_ONLIST;
+ txd->refs = 1;
+ }
+ return txd;
+}
+
+static __inline void
+hn_txdesc_hold(struct hn_txdesc *txd)
+{
+
+ /* 0->1 transition will never work */
+ KASSERT(txd->refs > 0, ("invalid refs %d", txd->refs));
+ atomic_add_int(&txd->refs, 1);
+}
+
/*
* Send completion processing
*
@@ -503,34 +717,46 @@ netvsc_shutdown(device_t dev)
void
netvsc_xmit_completion(void *context)
{
- netvsc_packet *packet = (netvsc_packet *)context;
- struct mbuf *mb;
- uint8_t *buf;
+ netvsc_packet *packet = context;
+ struct hn_txdesc *txd;
+ struct hn_softc *sc;
- mb = (struct mbuf *)(uintptr_t)packet->compl.send.send_completion_tid;
- buf = ((uint8_t *)packet) - HV_NV_PACKET_OFFSET_IN_BUF;
+ txd = (struct hn_txdesc *)(uintptr_t)
+ packet->compl.send.send_completion_tid;
- free(buf, M_NETVSC);
+ sc = txd->sc;
+ sc->hn_txeof = 1;
+ hn_txdesc_put(sc, txd);
+}
- if (mb != NULL) {
- m_freem(mb);
- }
+void
+netvsc_channel_rollup(struct hv_device *device_ctx)
+{
+ struct hn_softc *sc = device_get_softc(device_ctx->device);
+ struct ifnet *ifp;
+
+ if (!sc->hn_txeof)
+ return;
+
+ sc->hn_txeof = 0;
+ ifp = sc->hn_ifp;
+ NV_LOCK(sc);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ hn_start_locked(ifp);
+ NV_UNLOCK(sc);
}
/*
* Start a transmit of one or more packets
*/
-static int
+static void
hn_start_locked(struct ifnet *ifp)
{
hn_softc_t *sc = ifp->if_softc;
struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
netvsc_dev *net_dev = sc->net_dev;
- device_t dev = device_ctx->device;
- uint8_t *buf;
netvsc_packet *packet;
struct mbuf *m_head, *m;
- struct mbuf *mc_head = NULL;
struct ether_vlan_header *eh;
rndis_msg *rndis_mesg;
rndis_packet *rndis_pkt;
@@ -539,84 +765,40 @@ hn_start_locked(struct ifnet *ifp)
rndis_tcp_ip_csum_info *csum_info;
rndis_tcp_tso_info *tso_info;
int ether_len;
- int i;
- int num_frags;
- int len;
- int retries = 0;
- int ret = 0;
uint32_t rndis_msg_size = 0;
uint32_t trans_proto_type;
uint32_t send_buf_section_idx =
NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX;
- while (!IFQ_DRV_IS_EMPTY(&sc->hn_ifp->if_snd)) {
- IFQ_DRV_DEQUEUE(&sc->hn_ifp->if_snd, m_head);
- if (m_head == NULL) {
- break;
- }
-
- len = 0;
- num_frags = 0;
-
- /* Walk the mbuf list computing total length and num frags */
- for (m = m_head; m != NULL; m = m->m_next) {
- if (m->m_len != 0) {
- num_frags++;
- len += m->m_len;
- }
- }
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING)
+ return;
- /*
- * Reserve the number of pages requested. Currently,
- * one page is reserved for the message in the RNDIS
- * filter packet
- */
- num_frags += HV_RF_NUM_TX_RESERVED_PAGE_BUFS;
+ while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+ bus_dma_segment_t segs[HN_TX_DATA_SEGCNT_MAX];
+ int error, nsegs, i, send_failed = 0;
+ struct hn_txdesc *txd;
- /* If exceeds # page_buffers in netvsc_packet */
- if (num_frags > NETVSC_PACKET_MAXPAGE) {
- device_printf(dev, "exceed max page buffers,%d,%d\n",
- num_frags, NETVSC_PACKET_MAXPAGE);
- m_freem(m_head);
- if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
- return (EINVAL);
- }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL)
+ break;
- /*
- * Allocate a buffer with space for a netvsc packet plus a
- * number of reserved areas. First comes a (currently 16
- * bytes, currently unused) reserved data area. Second is
- * the netvsc_packet. Third is an area reserved for an
- * rndis_filter_packet struct. Fourth (optional) is a
- * rndis_per_packet_info struct.
- * Changed malloc to M_NOWAIT to avoid sleep under spin lock.
- * No longer reserving extra space for page buffers, as they
- * are already part of the netvsc_packet.
- */
- buf = malloc(HV_NV_PACKET_OFFSET_IN_BUF +
- sizeof(netvsc_packet) +
- sizeof(rndis_msg) +
- RNDIS_VLAN_PPI_SIZE +
- RNDIS_TSO_PPI_SIZE +
- RNDIS_CSUM_PPI_SIZE,
- M_NETVSC, M_ZERO | M_NOWAIT);
- if (buf == NULL) {
- device_printf(dev, "hn:malloc packet failed\n");
- m_freem(m_head);
- if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
- return (ENOMEM);
+ txd = hn_txdesc_get(sc);
+ if (txd == NULL) {
+ sc->hn_no_txdescs++;
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
}
- packet = (netvsc_packet *)(buf + HV_NV_PACKET_OFFSET_IN_BUF);
- *(vm_offset_t *)buf = HV_NV_SC_PTR_OFFSET_IN_BUF;
+ packet = &txd->netvsc_pkt;
+ /* XXX not necessary */
+ memset(packet, 0, sizeof(*packet));
packet->is_data_pkt = TRUE;
- /* Set up the rndis header */
- packet->page_buf_count = num_frags;
-
/* Initialize it from the mbuf */
- packet->tot_data_buf_len = len;
+ packet->tot_data_buf_len = m_head->m_pkthdr.len;
/*
* extension points to the area reserved for the
@@ -624,8 +806,9 @@ hn_start_locked(struct ifnet *ifp)
* the netvsc_packet (and rppi struct, if present;
* length is updated later).
*/
- packet->rndis_mesg = packet + 1;
- rndis_mesg = (rndis_msg *)packet->rndis_mesg;
+ rndis_mesg = txd->rndis_msg;
+ /* XXX not necessary */
+ memset(rndis_mesg, 0, HN_RNDIS_MSG_LEN);
rndis_mesg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG;
rndis_pkt = &rndis_mesg->msg.packet;
@@ -644,8 +827,6 @@ hn_start_locked(struct ifnet *ifp)
* set up some additional fields so the Hyper-V infrastructure will stuff the VLAN tag
* into the frame.
*/
- packet->vlan_tci = m_head->m_pkthdr.ether_vtag;
-
rndis_msg_size += RNDIS_VLAN_PPI_SIZE;
rppi = hv_set_rppi_data(rndis_mesg, RNDIS_VLAN_PPI_SIZE,
@@ -656,7 +837,7 @@ hn_start_locked(struct ifnet *ifp)
rppi->per_packet_info_offset);
/* FreeBSD does not support CFI or priority */
rppi_vlan_info->u1.s1.vlan_id =
- packet->vlan_tci & 0xfff;
+ m_head->m_pkthdr.ether_vtag & 0xfff;
}
/* Only check the flags for outbound and ignore the ones for inbound */
@@ -758,7 +939,7 @@ pre_send:
packet->tot_data_buf_len = rndis_mesg->msg_len;
/* send packet with send buffer */
- if (packet->tot_data_buf_len < net_dev->send_section_size) {
+ if (packet->tot_data_buf_len < sc->hn_tx_chimney_size) {
send_buf_section_idx =
hv_nv_get_next_send_section(net_dev);
if (send_buf_section_idx !=
@@ -783,33 +964,49 @@ pre_send:
packet->send_buf_section_size =
packet->tot_data_buf_len;
packet->page_buf_count = 0;
+ sc->hn_tx_chimney++;
goto do_send;
}
}
+ error = hn_txdesc_dmamap_load(sc, txd, &m_head, segs, &nsegs);
+ if (error) {
+ int freed;
+
+ /*
+ * This mbuf is not linked w/ the txd yet, so free
+ * it now.
+ */
+ m_freem(m_head);
+ freed = hn_txdesc_put(sc, txd);
+ KASSERT(freed != 0,
+ ("fail to free txd upon txdma error"));
+
+ sc->hn_txdma_failed++;
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ continue;
+ }
+
+ packet->page_buf_count = nsegs +
+ HV_RF_NUM_TX_RESERVED_PAGE_BUFS;
+
/* send packet with page buffer */
- packet->page_buffers[0].pfn =
- atop(hv_get_phys_addr(rndis_mesg));
+ packet->page_buffers[0].pfn = atop(txd->rndis_msg_paddr);
packet->page_buffers[0].offset =
- (unsigned long)rndis_mesg & PAGE_MASK;
+ txd->rndis_msg_paddr & PAGE_MASK;
packet->page_buffers[0].length = rndis_msg_size;
/*
* Fill the page buffers with mbuf info starting at index
* HV_RF_NUM_TX_RESERVED_PAGE_BUFS.
*/
- i = HV_RF_NUM_TX_RESERVED_PAGE_BUFS;
- for (m = m_head; m != NULL; m = m->m_next) {
- if (m->m_len) {
- vm_offset_t paddr =
- vtophys(mtod(m, vm_offset_t));
- packet->page_buffers[i].pfn =
- paddr >> PAGE_SHIFT;
- packet->page_buffers[i].offset =
- paddr & (PAGE_SIZE - 1);
- packet->page_buffers[i].length = m->m_len;
- i++;
- }
+ for (i = 0; i < nsegs; ++i) {
+ hv_vmbus_page_buffer *pb = &packet->page_buffers[
+ i + HV_RF_NUM_TX_RESERVED_PAGE_BUFS];
+
+ pb->pfn = atop(segs[i].ds_addr);
+ pb->offset = segs[i].ds_addr & PAGE_MASK;
+ pb->length = segs[i].ds_len;
}
packet->send_buf_section_idx =
@@ -817,63 +1014,65 @@ pre_send:
packet->send_buf_section_size = 0;
do_send:
+ txd->m = m_head;
- /*
- * If bpf, copy the mbuf chain. This is less expensive than
- * it appears; the mbuf clusters are not copied, only their
- * reference counts are incremented.
- * Needed to avoid a race condition where the completion
- * callback is invoked, freeing the mbuf chain, before the
- * bpf_mtap code has a chance to run.
- */
- if (ifp->if_bpf) {
- mc_head = m_copypacket(m_head, M_DONTWAIT);
- }
-retry_send:
/* Set the completion routine */
packet->compl.send.on_send_completion = netvsc_xmit_completion;
packet->compl.send.send_completion_context = packet;
- packet->compl.send.send_completion_tid = (uint64_t)(uintptr_t)m_head;
-
- /* Removed critical_enter(), does not appear necessary */
- ret = hv_nv_on_send(device_ctx, packet);
- if (ret == 0) {
- ifp->if_opackets++;
- /* if bpf && mc_head, call bpf_mtap code */
- if (mc_head) {
- ETHER_BPF_MTAP(ifp, mc_head);
- }
- } else {
- retries++;
- if (retries < 4) {
- goto retry_send;
- }
+ packet->compl.send.send_completion_tid =
+ (uint64_t)(uintptr_t)txd;
- IF_PREPEND(&ifp->if_snd, m_head);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+again:
+ /*
+ * Make sure that txd is not freed before ETHER_BPF_MTAP.
+ */
+ hn_txdesc_hold(txd);
+ error = hv_nv_on_send(device_ctx, packet);
+ if (!error) {
+ ETHER_BPF_MTAP(ifp, m_head);
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ }
+ hn_txdesc_put(sc, txd);
+
+ if (__predict_false(error)) {
+ int freed;
/*
- * Null the mbuf pointer so the completion function
- * does not free the mbuf chain. We just pushed the
- * mbuf chain back on the if_snd queue.
+ * This should "really rarely" happen.
+ *
+ * XXX Too many RX to be acked or too many sideband
+ * commands to run? Ask netvsc_channel_rollup()
+ * to kick start later.
*/
- packet->compl.send.send_completion_tid = 0;
+ sc->hn_txeof = 1;
+ if (!send_failed) {
+ sc->hn_send_failed++;
+ send_failed = 1;
+ /*
+ * Try sending again after set hn_txeof;
+ * in case that we missed the last
+ * netvsc_channel_rollup().
+ */
+ goto again;
+ }
+ if_printf(ifp, "send failed\n");
/*
- * Release the resources since we will not get any
- * send completion
+ * This mbuf will be prepended, don't free it
+ * in hn_txdesc_put(); only unload it from the
+ * DMA map in hn_txdesc_put(), if it was loaded.
*/
- netvsc_xmit_completion(packet);
- if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
- }
+ txd->m = NULL;
+ freed = hn_txdesc_put(sc, txd);
+ KASSERT(freed != 0,
+ ("fail to free txd upon send error"));
- /* if bpf && mc_head, free the mbuf chain copy */
- if (mc_head) {
- m_freem(mc_head);
+ sc->hn_send_failed++;
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
}
}
-
- return (ret);
}
/*
@@ -1222,6 +1421,9 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
}
+ sc->hn_tx_chimney_max = sc->net_dev->send_section_size;
+ if (sc->hn_tx_chimney_size > sc->hn_tx_chimney_max)
+ sc->hn_tx_chimney_size = sc->hn_tx_chimney_max;
hn_ifinit_locked(sc);
NV_LOCK(sc);
@@ -1479,6 +1681,25 @@ hn_lro_hiwat_sysctl(SYSCTL_HANDLER_ARGS)
#endif /* HN_LRO_HIWAT */
static int
+hn_tx_chimney_size_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct hn_softc *sc = arg1;
+ int chimney_size, error;
+
+ chimney_size = sc->hn_tx_chimney_size;
+ error = sysctl_handle_int(oidp, &chimney_size, 0, req);
+ if (error || req->newptr == NULL)
+ return error;
+
+ if (chimney_size > sc->hn_tx_chimney_max || chimney_size <= 0)
+ return EINVAL;
+
+ if (sc->hn_tx_chimney_size != chimney_size)
+ sc->hn_tx_chimney_size = chimney_size;
+ return 0;
+}
+
+static int
hn_check_iplen(const struct mbuf *m, int hoff)
{
const struct ip *ip;
@@ -1553,6 +1774,150 @@ hn_check_iplen(const struct mbuf *m, int hoff)
return ip->ip_p;
}
+static void
+hn_dma_map_paddr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ bus_addr_t *paddr = arg;
+
+ if (error)
+ return;
+
+ KASSERT(nseg == 1, ("too many segments %d!", nseg));
+ *paddr = segs->ds_addr;
+}
+
+static int
+hn_create_tx_ring(struct hn_softc *sc)
+{
+ bus_dma_tag_t parent_dtag;
+ int error, i;
+
+ sc->hn_txdesc_cnt = HN_TX_DESC_CNT;
+ sc->hn_txdesc = malloc(sizeof(struct hn_txdesc) * sc->hn_txdesc_cnt,
+ M_NETVSC, M_WAITOK | M_ZERO);
+ SLIST_INIT(&sc->hn_txlist);
+ mtx_init(&sc->hn_txlist_spin, "hn txlist", NULL, MTX_SPIN);
+
+ parent_dtag = bus_get_dma_tag(sc->hn_dev);
+
+ /* DMA tag for RNDIS messages. */
+ error = bus_dma_tag_create(parent_dtag, /* parent */
+ HN_RNDIS_MSG_ALIGN, /* alignment */
+ HN_RNDIS_MSG_BOUNDARY, /* boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ HN_RNDIS_MSG_LEN, /* maxsize */
+ 1, /* nsegments */
+ HN_RNDIS_MSG_LEN, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockfuncarg */
+ &sc->hn_tx_rndis_dtag);
+ if (error) {
+ device_printf(sc->hn_dev, "failed to create rndis dmatag\n");
+ return error;
+ }
+
+ /* DMA tag for data. */
+ error = bus_dma_tag_create(parent_dtag, /* parent */
+ 1, /* alignment */
+ HN_TX_DATA_BOUNDARY, /* boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ HN_TX_DATA_MAXSIZE, /* maxsize */
+ HN_TX_DATA_SEGCNT_MAX, /* nsegments */
+ HN_TX_DATA_SEGSIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockfuncarg */
+ &sc->hn_tx_data_dtag);
+ if (error) {
+ device_printf(sc->hn_dev, "failed to create data dmatag\n");
+ return error;
+ }
+
+ for (i = 0; i < sc->hn_txdesc_cnt; ++i) {
+ struct hn_txdesc *txd = &sc->hn_txdesc[i];
+
+ txd->sc = sc;
+
+ /*
+ * Allocate and load RNDIS messages.
+ */
+ error = bus_dmamem_alloc(sc->hn_tx_rndis_dtag,
+ (void **)&txd->rndis_msg,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT,
+ &txd->rndis_msg_dmap);
+ if (error) {
+ device_printf(sc->hn_dev,
+ "failed to allocate rndis_msg, %d\n", i);
+ return error;
+ }
+
+ error = bus_dmamap_load(sc->hn_tx_rndis_dtag,
+ txd->rndis_msg_dmap,
+ txd->rndis_msg, HN_RNDIS_MSG_LEN,
+ hn_dma_map_paddr, &txd->rndis_msg_paddr,
+ BUS_DMA_NOWAIT);
+ if (error) {
+ device_printf(sc->hn_dev,
+ "failed to load rndis_msg, %d\n", i);
+ bus_dmamem_free(sc->hn_tx_rndis_dtag,
+ txd->rndis_msg, txd->rndis_msg_dmap);
+ return error;
+ }
+
+ /* DMA map for TX data. */
+ error = bus_dmamap_create(sc->hn_tx_data_dtag, 0,
+ &txd->data_dmap);
+ if (error) {
+ device_printf(sc->hn_dev,
+ "failed to allocate tx data dmamap\n");
+ bus_dmamap_unload(sc->hn_tx_rndis_dtag,
+ txd->rndis_msg_dmap);
+ bus_dmamem_free(sc->hn_tx_rndis_dtag,
+ txd->rndis_msg, txd->rndis_msg_dmap);
+ return error;
+ }
+
+ /* All set, put it to list */
+ txd->flags |= HN_TXD_FLAG_ONLIST;
+ SLIST_INSERT_HEAD(&sc->hn_txlist, txd, link);
+ }
+ sc->hn_txdesc_avail = sc->hn_txdesc_cnt;
+
+ return 0;
+}
+
+static void
+hn_destroy_tx_ring(struct hn_softc *sc)
+{
+ struct hn_txdesc *txd;
+
+ while ((txd = SLIST_FIRST(&sc->hn_txlist)) != NULL) {
+ KASSERT(txd->m == NULL, ("still has mbuf installed"));
+ KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0,
+ ("still dma mapped"));
+ SLIST_REMOVE_HEAD(&sc->hn_txlist, link);
+
+ bus_dmamap_unload(sc->hn_tx_rndis_dtag,
+ txd->rndis_msg_dmap);
+ bus_dmamem_free(sc->hn_tx_rndis_dtag,
+ txd->rndis_msg, txd->rndis_msg_dmap);
+
+ bus_dmamap_destroy(sc->hn_tx_data_dtag, txd->data_dmap);
+ }
+
+ if (sc->hn_tx_data_dtag != NULL)
+ bus_dma_tag_destroy(sc->hn_tx_data_dtag);
+ if (sc->hn_tx_rndis_dtag != NULL)
+ bus_dma_tag_destroy(sc->hn_tx_rndis_dtag);
+ free(sc->hn_txdesc, M_NETVSC);
+ mtx_destroy(&sc->hn_txlist_spin);
+}
+
static device_method_t netvsc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, netvsc_probe),
diff --git a/sys/dev/hyperv/netvsc/hv_rndis.h b/sys/dev/hyperv/netvsc/hv_rndis.h
index fd032de..cd46ecc 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis.h
+++ b/sys/dev/hyperv/netvsc/hv_rndis.h
@@ -1050,6 +1050,7 @@ int netvsc_recv(struct hv_device *device_ctx,
netvsc_packet *packet,
rndis_tcp_ip_csum_info *csum_info);
void netvsc_recv_rollup(struct hv_device *device_ctx);
+void netvsc_channel_rollup(struct hv_device *device_ctx);
void* hv_set_rppi_data(rndis_msg *rndis_mesg,
uint32_t rppi_size,
diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.c b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
index 3e95024..dfd0b47 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis_filter.c
+++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
@@ -974,3 +974,21 @@ hv_rf_receive_rollup(netvsc_dev *net_dev)
rndis_dev = (rndis_device *)net_dev->extension;
netvsc_recv_rollup(rndis_dev->net_dev->dev);
}
+
+void
+hv_rf_channel_rollup(netvsc_dev *net_dev)
+{
+ rndis_device *rndis_dev;
+
+ rndis_dev = (rndis_device *)net_dev->extension;
+
+ /*
+ * This could be called pretty early, so we need
+ * to make sure everything has been setup.
+ */
+ if (rndis_dev == NULL ||
+ rndis_dev->net_dev == NULL ||
+ rndis_dev->net_dev->dev == NULL)
+ return;
+ netvsc_channel_rollup(rndis_dev->net_dev->dev);
+}
diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.h b/sys/dev/hyperv/netvsc/hv_rndis_filter.h
index 2f3ebd8..9d7a38d 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis_filter.h
+++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.h
@@ -99,6 +99,7 @@ typedef struct rndis_device_ {
int hv_rf_on_receive(netvsc_dev *net_dev,
struct hv_device *device, netvsc_packet *pkt);
void hv_rf_receive_rollup(netvsc_dev *net_dev);
+void hv_rf_channel_rollup(netvsc_dev *net_dev);
int hv_rf_on_device_add(struct hv_device *device, void *additl_info);
int hv_rf_on_device_remove(struct hv_device *device, boolean_t destroy_channel);
int hv_rf_on_open(struct hv_device *device);
diff --git a/sys/dev/hyperv/vmbus/hv_channel_mgmt.c b/sys/dev/hyperv/vmbus/hv_channel_mgmt.c
index c7f3538..4ccb647 100644
--- a/sys/dev/hyperv/vmbus/hv_channel_mgmt.c
+++ b/sys/dev/hyperv/vmbus/hv_channel_mgmt.c
@@ -274,14 +274,16 @@ vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
boolean_t f_new;
hv_vmbus_channel* channel;
int ret;
+ uint32_t relid;
f_new = TRUE;
channel = NULL;
-
+ relid = new_channel->offer_msg.child_rel_id;
/*
* Make sure this is a new offer
*/
mtx_lock(&hv_vmbus_g_connection.channel_lock);
+ hv_vmbus_g_connection.channels[relid] = new_channel;
TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor,
list_entry)
@@ -325,16 +327,18 @@ vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
mtx_unlock(&channel->sc_lock);
/* Insert new channel into channel_anchor. */
- printf("Storvsc get multi-channel offer, rel=%u.\n",
- new_channel->offer_msg.child_rel_id);
+ printf("VMBUS get multi-channel offer, rel=%u,sub=%u\n",
+ new_channel->offer_msg.child_rel_id,
+ new_channel->offer_msg.offer.sub_channel_index);
mtx_lock(&hv_vmbus_g_connection.channel_lock);
TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor,
new_channel, list_entry);
mtx_unlock(&hv_vmbus_g_connection.channel_lock);
if(bootverbose)
- printf("VMBUS: new multi-channel offer <%p>.\n",
- new_channel);
+ printf("VMBUS: new multi-channel offer <%p>, "
+ "its primary channel is <%p>.\n",
+ new_channel, new_channel->primary_channel);
/*XXX add it to percpu_list */
@@ -524,11 +528,14 @@ vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr)
rescind = (hv_vmbus_channel_rescind_offer*) hdr;
- channel = hv_vmbus_get_channel_from_rel_id(rescind->child_rel_id);
+ channel = hv_vmbus_g_connection.channels[rescind->child_rel_id];
if (channel == NULL)
return;
hv_vmbus_child_device_unregister(channel->device);
+ mtx_lock(&hv_vmbus_g_connection.channel_lock);
+ hv_vmbus_g_connection.channels[rescind->child_rel_id] = NULL;
+ mtx_unlock(&hv_vmbus_g_connection.channel_lock);
}
/**
@@ -782,6 +789,8 @@ hv_vmbus_release_unattached_channels(void)
hv_vmbus_child_device_unregister(channel->device);
hv_vmbus_free_vmbus_channel(channel);
}
+ bzero(hv_vmbus_g_connection.channels,
+ sizeof(hv_vmbus_channel*) * HV_CHANNEL_MAX_COUNT);
mtx_unlock(&hv_vmbus_g_connection.channel_lock);
}
diff --git a/sys/dev/hyperv/vmbus/hv_connection.c b/sys/dev/hyperv/vmbus/hv_connection.c
index 7496288..a9e3561 100644
--- a/sys/dev/hyperv/vmbus/hv_connection.c
+++ b/sys/dev/hyperv/vmbus/hv_connection.c
@@ -232,6 +232,9 @@ hv_vmbus_connect(void) {
goto cleanup;
}
+ hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
+ HV_CHANNEL_MAX_COUNT,
+ M_DEVBUF, M_WAITOK | M_ZERO);
/*
* Find the highest vmbus version number we can support.
*/
@@ -295,6 +298,7 @@ hv_vmbus_connect(void) {
free(msg_info, M_DEVBUF);
}
+ free(hv_vmbus_g_connection.channels, M_DEVBUF);
return (ret);
}
@@ -325,6 +329,7 @@ hv_vmbus_disconnect(void) {
hv_work_queue_close(hv_vmbus_g_connection.work_queue);
sema_destroy(&hv_vmbus_g_connection.control_sema);
+ free(hv_vmbus_g_connection.channels, M_DEVBUF);
hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
free(msg, M_DEVBUF);
@@ -333,35 +338,6 @@ hv_vmbus_disconnect(void) {
}
/**
- * Get the channel object given its child relative id (ie channel id)
- */
-hv_vmbus_channel*
-hv_vmbus_get_channel_from_rel_id(uint32_t rel_id) {
-
- hv_vmbus_channel* channel;
- hv_vmbus_channel* foundChannel = NULL;
-
- /*
- * TODO:
- * Consider optimization where relids are stored in a fixed size array
- * and channels are accessed without the need to take this lock or search
- * the list.
- */
- mtx_lock(&hv_vmbus_g_connection.channel_lock);
- TAILQ_FOREACH(channel,
- &hv_vmbus_g_connection.channel_anchor, list_entry) {
-
- if (channel->offer_msg.child_rel_id == rel_id) {
- foundChannel = channel;
- break;
- }
- }
- mtx_unlock(&hv_vmbus_g_connection.channel_lock);
-
- return (foundChannel);
-}
-
-/**
* Process a channel event notification
*/
static void
@@ -377,7 +353,7 @@ VmbusProcessChannelEvent(uint32_t relid)
* the channel callback to process the event
*/
- channel = hv_vmbus_get_channel_from_rel_id(relid);
+ channel = hv_vmbus_g_connection.channels[relid];
if (channel == NULL) {
return;
@@ -473,7 +449,7 @@ hv_vmbus_on_events(void *arg)
if (recv_interrupt_page != NULL) {
for (dword = 0; dword < maxdword; dword++) {
if (recv_interrupt_page[dword]) {
- for (bit = 0; bit < 32; bit++) {
+ for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
if (synch_test_and_clear_bit(bit,
(uint32_t *) &recv_interrupt_page[dword])) {
rel_id = (dword << 5) + bit;
diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h
index 74fe824..13a35c4 100644
--- a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h
+++ b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h
@@ -58,6 +58,12 @@ typedef uint16_t hv_vmbus_status;
#define HV_EVENT_FLAGS_BYTE_COUNT (256)
#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(uint32_t))
+/**
+ * max channel count <== event_flags_dword_count * bit_of_dword
+ */
+#define HV_CHANNEL_DWORD_LEN (32)
+#define HV_CHANNEL_MAX_COUNT \
+ ((HV_EVENT_FLAGS_DWORD_COUNT) * HV_CHANNEL_DWORD_LEN)
/*
* MessageId: HV_STATUS_INSUFFICIENT_BUFFERS
* MessageText:
@@ -355,6 +361,10 @@ typedef struct {
TAILQ_HEAD(, hv_vmbus_channel) channel_anchor;
struct mtx channel_lock;
+ /**
+ * channel table for fast lookup through id.
+ */
+ hv_vmbus_channel **channels;
hv_vmbus_handle work_queue;
struct sema control_sema;
} hv_vmbus_connection;
@@ -699,7 +709,6 @@ int hv_vmbus_child_device_register(
struct hv_device *child_dev);
int hv_vmbus_child_device_unregister(
struct hv_device *child_dev);
-hv_vmbus_channel* hv_vmbus_get_channel_from_rel_id(uint32_t rel_id);
/**
* Connection interfaces
OpenPOWER on IntegriCloud