summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.h9
-rw-r--r--sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c158
-rw-r--r--sys/dev/hyperv/vmbus/hv_channel.c33
3 files changed, 141 insertions, 59 deletions
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h
index e684cc5..22d975d 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.h
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h
@@ -39,9 +39,11 @@
#define __HV_NET_VSC_H__
#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/queue.h>
+#include <sys/taskqueue.h>
#include <sys/sx.h>
#include <machine/bus.h>
@@ -1009,7 +1011,6 @@ 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;
@@ -1018,9 +1019,15 @@ typedef struct hn_softc {
struct mtx hn_txlist_spin;
struct hn_txdesc_list hn_txlist;
+ int hn_txdesc_cnt;
int hn_txdesc_avail;
int hn_txeof;
+ int hn_direct_tx_size;
+ struct taskqueue *hn_tx_taskq;
+ struct task hn_start_task;
+ struct task hn_txeof_task;
+
struct lro_ctrl hn_lro;
int hn_lro_hiwat;
diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
index b3360ea..223d4a2 100644
--- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
+++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
@@ -146,6 +146,8 @@ __FBSDID("$FreeBSD$");
#define HN_TX_DATA_SEGCNT_MAX \
(NETVSC_PACKET_MAXPAGE - HV_RF_NUM_TX_RESERVED_PAGE_BUFS)
+#define HN_DIRECT_TX_SIZE_DEF 128
+
struct hn_txdesc {
SLIST_ENTRY(hn_txdesc) link;
struct mbuf *m;
@@ -194,6 +196,7 @@ struct hn_txdesc {
#define NV_LOCK_INIT(_sc, _name) \
mtx_init(&(_sc)->hn_lock, _name, MTX_NETWORK_LOCK, MTX_DEF)
#define NV_LOCK(_sc) mtx_lock(&(_sc)->hn_lock)
+#define NV_TRYLOCK(_sc) mtx_trylock(&(_sc)->hn_lock)
#define NV_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->hn_lock, MA_OWNED)
#define NV_UNLOCK(_sc) mtx_unlock(&(_sc)->hn_lock)
#define NV_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->hn_lock)
@@ -206,7 +209,7 @@ struct hn_txdesc {
int hv_promisc_mode = 0; /* normal mode by default */
/* Trust tcp segements verification on host side. */
-static int hn_trust_hosttcp = 0;
+static int hn_trust_hosttcp = 1;
TUNABLE_INT("dev.hn.trust_hosttcp", &hn_trust_hosttcp);
#if __FreeBSD_version >= 1100045
@@ -219,6 +222,10 @@ TUNABLE_INT("dev.hn.tso_maxlen", &hn_tso_maxlen);
static int hn_tx_chimney_size = 0;
TUNABLE_INT("dev.hn.tx_chimney_size", &hn_tx_chimney_size);
+/* Limit the size of packet for direct transmission */
+static int hn_direct_tx_size = HN_DIRECT_TX_SIZE_DEF;
+TUNABLE_INT("dev.hn.direct_tx_size", &hn_direct_tx_size);
+
/*
* Forward declarations
*/
@@ -226,8 +233,9 @@ 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 void hn_start_locked(struct ifnet *ifp);
+static int hn_start_locked(struct ifnet *ifp, int len);
static void hn_start(struct ifnet *ifp);
+static void hn_start_txeof(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
@@ -237,6 +245,8 @@ 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 void hn_start_taskfunc(void *xsc, int pending);
+static void hn_txeof_taskfunc(void *xsc, int pending);
static __inline void
hn_set_lro_hiwat(struct hn_softc *sc, int hiwat)
@@ -384,6 +394,14 @@ netvsc_attach(device_t dev)
sc->hn_dev = dev;
sc->hn_lro_hiwat = HN_LRO_HIWAT_DEF;
sc->hn_trust_hosttcp = hn_trust_hosttcp;
+ sc->hn_direct_tx_size = hn_direct_tx_size;
+
+ sc->hn_tx_taskq = taskqueue_create_fast("hn_tx", M_WAITOK,
+ taskqueue_thread_enqueue, &sc->hn_tx_taskq);
+ taskqueue_start_threads(&sc->hn_tx_taskq, 1, PI_NET, "%s tx",
+ device_get_nameunit(dev));
+ TASK_INIT(&sc->hn_start_task, 0, hn_start_taskfunc, sc);
+ TASK_INIT(&sc->hn_txeof_task, 0, hn_txeof_taskfunc, sc);
error = hn_create_tx_ring(sc);
if (error)
@@ -524,6 +542,9 @@ netvsc_attach(device_t dev)
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");
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "direct_tx_size",
+ CTLFLAG_RW, &sc->hn_direct_tx_size, 0,
+ "Size of the packet for direct transmission");
if (unit == 0) {
struct sysctl_ctx_list *dc_ctx;
@@ -548,6 +569,9 @@ netvsc_attach(device_t dev)
SYSCTL_ADD_INT(dc_ctx, dc_child, OID_AUTO, "tso_maxlen",
CTLFLAG_RD, &hn_tso_maxlen, 0, "TSO burst limit");
#endif
+ SYSCTL_ADD_INT(dc_ctx, dc_child, OID_AUTO, "direct_tx_size",
+ CTLFLAG_RD, &hn_direct_tx_size, 0,
+ "Size of the packet for direct transmission");
}
return (0);
@@ -583,6 +607,10 @@ netvsc_detach(device_t dev)
hv_rf_on_device_remove(hv_device, HV_RF_NV_DESTROY_CHANNEL);
+ taskqueue_drain(sc->hn_tx_taskq, &sc->hn_start_task);
+ taskqueue_drain(sc->hn_tx_taskq, &sc->hn_txeof_task);
+ taskqueue_free(sc->hn_tx_taskq);
+
ifmedia_removeall(&sc->hn_media);
#if defined(INET) || defined(INET6)
tcp_lro_free(&sc->hn_lro);
@@ -733,30 +761,23 @@ 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);
+ hn_start_txeof(sc->hn_ifp);
}
/*
* Start a transmit of one or more packets
*/
-static void
-hn_start_locked(struct ifnet *ifp)
+static int
+hn_start_locked(struct ifnet *ifp, int len)
{
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;
- netvsc_packet *packet;
- struct mbuf *m_head, *m;
struct ether_vlan_header *eh;
rndis_msg *rndis_mesg;
rndis_packet *rndis_pkt;
@@ -767,36 +788,42 @@ hn_start_locked(struct ifnet *ifp)
int ether_len;
uint32_t rndis_msg_size = 0;
uint32_t trans_proto_type;
- uint32_t send_buf_section_idx =
- NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX;
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING)
- return;
+ return 0;
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;
+ netvsc_packet *packet;
+ struct mbuf *m_head;
IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL)
break;
+ if (len > 0 && m_head->m_pkthdr.len > len) {
+ /*
+ * This sending could be time consuming; let callers
+ * dispatch this packet sending (and sending of any
+ * following up packets) to tx taskqueue.
+ */
+ IF_PREPEND(&ifp->if_snd, m_head);
+ return 1;
+ }
+
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;
+ atomic_set_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
break;
}
packet = &txd->netvsc_pkt;
- /* XXX not necessary */
- memset(packet, 0, sizeof(*packet));
-
packet->is_data_pkt = TRUE;
-
/* Initialize it from the mbuf */
packet->tot_data_buf_len = m_head->m_pkthdr.len;
@@ -940,24 +967,21 @@ pre_send:
/* send packet with send buffer */
if (packet->tot_data_buf_len < sc->hn_tx_chimney_size) {
+ uint32_t send_buf_section_idx;
+
send_buf_section_idx =
hv_nv_get_next_send_section(net_dev);
if (send_buf_section_idx !=
NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX) {
- char *dest = ((char *)net_dev->send_buf +
- send_buf_section_idx *
- net_dev->send_section_size);
+ uint8_t *dest = ((uint8_t *)net_dev->send_buf +
+ (send_buf_section_idx *
+ net_dev->send_section_size));
memcpy(dest, rndis_mesg, rndis_msg_size);
dest += rndis_msg_size;
- for (m = m_head; m != NULL; m = m->m_next) {
- if (m->m_len) {
- memcpy(dest,
- (void *)mtod(m, vm_offset_t),
- m->m_len);
- dest += m->m_len;
- }
- }
+
+ m_copydata(m_head, 0, m_head->m_pkthdr.len,
+ dest);
packet->send_buf_section_idx =
send_buf_section_idx;
@@ -1069,10 +1093,11 @@ again:
sc->hn_send_failed++;
IF_PREPEND(&ifp->if_snd, m_head);
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ atomic_set_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
break;
}
}
+ return 0;
}
/*
@@ -1566,7 +1591,8 @@ hn_stop(hn_softc_t *sc)
if (bootverbose)
printf(" Closing Device ...\n");
- ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ atomic_clear_int(&ifp->if_drv_flags,
+ (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
if_link_state_change(ifp, LINK_STATE_DOWN);
sc->hn_initdone = 0;
@@ -1582,13 +1608,43 @@ hn_start(struct ifnet *ifp)
hn_softc_t *sc;
sc = ifp->if_softc;
- NV_LOCK(sc);
- if (sc->temp_unusable) {
+ if (NV_TRYLOCK(sc)) {
+ int sched;
+
+ sched = hn_start_locked(ifp, sc->hn_direct_tx_size);
NV_UNLOCK(sc);
- return;
+ if (!sched)
+ return;
+ }
+ taskqueue_enqueue_fast(sc->hn_tx_taskq, &sc->hn_start_task);
+}
+
+static void
+hn_start_txeof(struct ifnet *ifp)
+{
+ hn_softc_t *sc;
+
+ sc = ifp->if_softc;
+ if (NV_TRYLOCK(sc)) {
+ int sched;
+
+ atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
+ sched = hn_start_locked(ifp, sc->hn_direct_tx_size);
+ NV_UNLOCK(sc);
+ if (sched) {
+ taskqueue_enqueue_fast(sc->hn_tx_taskq,
+ &sc->hn_start_task);
+ }
+ } else {
+ /*
+ * Release the OACTIVE earlier, with the hope, that
+ * others could catch up. The task will clear the
+ * flag again with the NV_LOCK to avoid possible
+ * races.
+ */
+ atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
+ taskqueue_enqueue_fast(sc->hn_tx_taskq, &sc->hn_txeof_task);
}
- hn_start_locked(ifp);
- NV_UNLOCK(sc);
}
/*
@@ -1615,8 +1671,8 @@ hn_ifinit_locked(hn_softc_t *sc)
} else {
sc->hn_initdone = 1;
}
- ifp->if_drv_flags |= IFF_DRV_RUNNING;
- ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
+ atomic_set_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
if_link_state_change(ifp, LINK_STATE_UP);
}
@@ -1918,6 +1974,28 @@ hn_destroy_tx_ring(struct hn_softc *sc)
mtx_destroy(&sc->hn_txlist_spin);
}
+static void
+hn_start_taskfunc(void *xsc, int pending __unused)
+{
+ struct hn_softc *sc = xsc;
+
+ NV_LOCK(sc);
+ hn_start_locked(sc->hn_ifp, 0);
+ NV_UNLOCK(sc);
+}
+
+static void
+hn_txeof_taskfunc(void *xsc, int pending __unused)
+{
+ struct hn_softc *sc = xsc;
+ struct ifnet *ifp = sc->hn_ifp;
+
+ NV_LOCK(sc);
+ atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
+ hn_start_locked(ifp, 0);
+ NV_UNLOCK(sc);
+}
+
static device_method_t netvsc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, netvsc_probe),
diff --git a/sys/dev/hyperv/vmbus/hv_channel.c b/sys/dev/hyperv/vmbus/hv_channel.c
index 7037768..2e652cc 100644
--- a/sys/dev/hyperv/vmbus/hv_channel.c
+++ b/sys/dev/hyperv/vmbus/hv_channel.c
@@ -666,11 +666,11 @@ hv_vmbus_channel_send_packet_pagebuffer(
{
int ret = 0;
- int i = 0;
boolean_t need_sig;
uint32_t packet_len;
+ uint32_t page_buflen;
uint32_t packetLen_aligned;
- hv_vmbus_sg_buffer_list buffer_list[3];
+ hv_vmbus_sg_buffer_list buffer_list[4];
hv_vmbus_channel_packet_page_buffer desc;
uint32_t descSize;
uint64_t alignedData = 0;
@@ -682,36 +682,33 @@ hv_vmbus_channel_send_packet_pagebuffer(
* Adjust the size down since hv_vmbus_channel_packet_page_buffer
* is the largest size we support
*/
- descSize = sizeof(hv_vmbus_channel_packet_page_buffer) -
- ((HV_MAX_PAGE_BUFFER_COUNT - page_count) *
- sizeof(hv_vmbus_page_buffer));
- packet_len = descSize + buffer_len;
+ descSize = __offsetof(hv_vmbus_channel_packet_page_buffer, range);
+ page_buflen = sizeof(hv_vmbus_page_buffer) * page_count;
+ packet_len = descSize + page_buflen + buffer_len;
packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
/* Setup the descriptor */
desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
- desc.data_offset8 = descSize >> 3; /* in 8-bytes granularity */
+ /* in 8-bytes granularity */
+ desc.data_offset8 = (descSize + page_buflen) >> 3;
desc.length8 = (uint16_t) (packetLen_aligned >> 3);
desc.transaction_id = request_id;
desc.range_count = page_count;
- for (i = 0; i < page_count; i++) {
- desc.range[i].length = page_buffers[i].length;
- desc.range[i].offset = page_buffers[i].offset;
- desc.range[i].pfn = page_buffers[i].pfn;
- }
-
buffer_list[0].data = &desc;
buffer_list[0].length = descSize;
- buffer_list[1].data = buffer;
- buffer_list[1].length = buffer_len;
+ buffer_list[1].data = page_buffers;
+ buffer_list[1].length = page_buflen;
- buffer_list[2].data = &alignedData;
- buffer_list[2].length = packetLen_aligned - packet_len;
+ buffer_list[2].data = buffer;
+ buffer_list[2].length = buffer_len;
- ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
+ buffer_list[3].data = &alignedData;
+ buffer_list[3].length = packetLen_aligned - packet_len;
+
+ ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 4,
&need_sig);
/* TODO: We should determine if this is optional */
OpenPOWER on IntegriCloud