Improve Hyper-V support.

vmbus(4):
 - Added support for multichannel.

hvn(4):
 - Added support for multichannel.
 - Added support for change MTU.
 - Added support for TX aggregation.
 - Improve VLAN support.
 - Improve checksum offload support.
This commit is contained in:
nonaka 2022-05-20 13:55:16 +00:00
parent b97019535c
commit fdd3eadbf8
9 changed files with 4213 additions and 675 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: hyperv.c,v 1.14 2021/12/23 04:06:51 yamaguchi Exp $ */
/* $NetBSD: hyperv.c,v 1.15 2022/05/20 13:55:16 nonaka Exp $ */
/*-
* Copyright (c) 2009-2012,2016-2017 Microsoft Corp.
@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
#ifdef __KERNEL_RCSID
__KERNEL_RCSID(0, "$NetBSD: hyperv.c,v 1.14 2021/12/23 04:06:51 yamaguchi Exp $");
__KERNEL_RCSID(0, "$NetBSD: hyperv.c,v 1.15 2022/05/20 13:55:16 nonaka Exp $");
#endif
#ifdef __FBSDID
__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hyperv.c 331757 2018-03-30 02:25:12Z emaste $");
@ -106,8 +106,6 @@ static char hyperv_hypercall_page[PAGE_SIZE]
static u_int hyperv_get_timecount(struct timecounter *);
static u_int hyperv_ver_major;
static u_int hyperv_features; /* CPUID_HV_MSR_ */
static u_int hyperv_recommends;

View File

@ -1,4 +1,4 @@
# $NetBSD: files.hyperv,v 1.2 2019/05/24 14:28:48 nonaka Exp $
# $NetBSD: files.hyperv,v 1.3 2022/05/20 13:55:17 nonaka Exp $
define hypervvmbus {}
device vmbus: hypervvmbus
@ -14,6 +14,11 @@ file dev/hyperv/hvkbd.c hvkbd needs-flag
device hvn: ether, ifnet, arp
attach hvn at hypervvmbus
file dev/hyperv/if_hvn.c hvn
defparam opt_if_hvn.h HVN_UDP_CKSUM_FIXUP_MTU_DEFAULT
HVN_CHANNEL_MAX_COUNT_DEFAULT
HVN_CHANNEL_COUNT_DEFAULT
HVN_TX_RING_COUNT_DEFAULT
HVN_LINK_STATE_CHANGE_DELAY
device hvs: scsi
attach hvs at hypervvmbus

View File

@ -1,4 +1,4 @@
/* $NetBSD: hyperv_common.c,v 1.5 2019/12/10 12:20:20 nonaka Exp $ */
/* $NetBSD: hyperv_common.c,v 1.6 2022/05/20 13:55:17 nonaka Exp $ */
/*-
* Copyright (c) 2009-2012,2016-2017 Microsoft Corp.
@ -29,7 +29,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: hyperv_common.c,v 1.5 2019/12/10 12:20:20 nonaka Exp $");
__KERNEL_RCSID(0, "$NetBSD: hyperv_common.c,v 1.6 2022/05/20 13:55:17 nonaka Exp $");
#include "hyperv.h"
@ -42,6 +42,7 @@ __KERNEL_RCSID(0, "$NetBSD: hyperv_common.c,v 1.5 2019/12/10 12:20:20 nonaka Exp
#include <dev/hyperv/hypervreg.h>
#include <dev/hyperv/hypervvar.h>
u_int hyperv_ver_major;
hyperv_tc64_t hyperv_tc64;
int hyperv_nullop(void);
@ -111,46 +112,40 @@ hyperv_guid2str(const struct hyperv_guid *guid, char *buf, size_t sz)
*/
void *
hyperv_dma_alloc(bus_dma_tag_t dmat, struct hyperv_dma *dma, bus_size_t size,
bus_size_t alignment, bus_size_t boundary, int nsegs, int flags)
bus_size_t alignment, bus_size_t boundary, int nsegs)
{
const int waitok = (flags & HYPERV_DMA_NOSLEEP) != HYPERV_DMA_NOSLEEP;
const int kmemflags = waitok ? KM_SLEEP: KM_NOSLEEP;
const int dmaflags = waitok ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT;
int rseg, error;
KASSERT(dma != NULL);
KASSERT(dma->segs == NULL);
KASSERT(nsegs > 0);
dma->segs = kmem_intr_zalloc(sizeof(*dma->segs) * nsegs, kmemflags);
if (dma->segs == NULL)
return NULL;
dma->segs = kmem_zalloc(sizeof(*dma->segs) * nsegs, KM_SLEEP);
dma->nsegs = nsegs;
error = bus_dmamem_alloc(dmat, size, alignment, boundary, dma->segs,
nsegs, &rseg, dmaflags);
nsegs, &rseg, BUS_DMA_WAITOK);
if (error) {
printf("%s: bus_dmamem_alloc failed: error=%d\n",
__func__, error);
goto fail1;
}
error = bus_dmamem_map(dmat, dma->segs, rseg, size, &dma->addr,
dmaflags);
BUS_DMA_WAITOK);
if (error) {
printf("%s: bus_dmamem_map failed: error=%d\n",
__func__, error);
goto fail2;
}
error = bus_dmamap_create(dmat, size, rseg, size, boundary, dmaflags,
&dma->map);
error = bus_dmamap_create(dmat, size, rseg, size, boundary,
BUS_DMA_WAITOK, &dma->map);
if (error) {
printf("%s: bus_dmamap_create failed: error=%d\n",
__func__, error);
goto fail3;
}
error = bus_dmamap_load(dmat, dma->map, dma->addr, size, NULL,
BUS_DMA_READ | BUS_DMA_WRITE | dmaflags);
BUS_DMA_READ | BUS_DMA_WRITE | BUS_DMA_WAITOK);
if (error) {
printf("%s: bus_dmamap_load failed: error=%d\n",
__func__, error);

View File

@ -1,4 +1,4 @@
/* $NetBSD: hypervreg.h,v 1.1 2019/02/15 08:54:01 nonaka Exp $ */
/* $NetBSD: hypervreg.h,v 1.2 2022/05/20 13:55:17 nonaka Exp $ */
/* $OpenBSD: hypervreg.h,v 1.10 2017/01/05 13:17:22 mikeb Exp $ */
/*-
@ -258,6 +258,7 @@ struct vmbus_bufring {
uint8_t br_rsvd[4084];
uint8_t br_data[0];
} __packed;
__CTASSERT(sizeof(struct vmbus_bufring) == PAGE_SIZE);
/*
* Channel

View File

@ -1,4 +1,4 @@
/* $NetBSD: hypervvar.h,v 1.5 2021/12/23 04:06:51 yamaguchi Exp $ */
/* $NetBSD: hypervvar.h,v 1.6 2022/05/20 13:55:17 nonaka Exp $ */
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
@ -59,6 +59,7 @@ __CTASSERT(sizeof(struct hyperv_reftsc) == PAGE_SIZE);
#endif
#if defined(_KERNEL)
extern u_int hyperv_ver_major;
int hyperv_hypercall_enabled(void);
int hyperv_synic_supported(void);
@ -108,10 +109,8 @@ hyperv_dma_get_paddr(struct hyperv_dma *dma)
return dma->map->dm_segs[0].ds_addr;
}
#define HYPERV_DMA_SLEEPOK 0
#define HYPERV_DMA_NOSLEEP __BIT(0)
void *hyperv_dma_alloc(bus_dma_tag_t, struct hyperv_dma *, bus_size_t,
bus_size_t, bus_size_t, int, int);
bus_size_t, bus_size_t, int);
void hyperv_dma_free(bus_dma_tag_t, struct hyperv_dma *);
#endif /* _KERNEL */

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_hvnreg.h,v 1.2 2021/10/21 13:21:54 andvar Exp $ */
/* $NetBSD: if_hvnreg.h,v 1.3 2022/05/20 13:55:17 nonaka Exp $ */
/* $OpenBSD: if_hvnreg.h,v 1.3 2016/09/14 17:48:28 mikeb Exp $ */
/*-
@ -201,4 +201,21 @@ struct hvn_nvs_rndis_ack {
uint8_t nvs_rsvd[24];
} __packed;
/*
* RNDIS extension
*/
/* Per-packet hash info */
#define HVN_NDIS_HASH_INFO_SIZE sizeof(uint32_t)
#define HVN_NDIS_PKTINFO_TYPE_HASHINF NDIS_PKTINFO_TYPE_ORIG_NBLIST
/* NDIS_HASH_ */
/* Per-packet hash value */
#define HVN_NDIS_HASH_VALUE_SIZE sizeof(uint32_t)
#define HVN_NDIS_PKTINFO_TYPE_HASHVAL NDIS_PKTINFO_TYPE_PKT_CANCELID
/* Per-packet-info size */
#define HVN_RNDIS_PKTINFO_SIZE(dlen) \
offsetof(struct rndis_pktinfo, rm_data[dlen])
#endif /* _IF_HVNREG_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: vmbus.c,v 1.17 2022/04/09 23:38:32 riastradh Exp $ */
/* $NetBSD: vmbus.c,v 1.18 2022/05/20 13:55:17 nonaka Exp $ */
/* $OpenBSD: hyperv.c,v 1.43 2017/06/27 13:56:15 mikeb Exp $ */
/*-
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.17 2022/04/09 23:38:32 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: vmbus.c,v 1.18 2022/05/20 13:55:17 nonaka Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -100,9 +100,6 @@ static struct vmbus_channel *
static int vmbus_channel_ring_create(struct vmbus_channel *, uint32_t);
static void vmbus_channel_ring_destroy(struct vmbus_channel *);
static void vmbus_channel_detach(struct vmbus_channel *);
static void vmbus_channel_pause(struct vmbus_channel *);
static uint32_t vmbus_channel_unpause(struct vmbus_channel *);
static uint32_t vmbus_channel_ready(struct vmbus_channel *);
static void vmbus_chevq_enqueue(struct vmbus_softc *, int, void *);
static void vmbus_process_chevq(void *);
static void vmbus_chevq_thread(void *);
@ -274,6 +271,9 @@ vmbus_attach(struct vmbus_softc *sc)
"hvmsg", NULL, IPL_NET, NULL, NULL, NULL);
hyperv_set_message_proc(vmbus_message_proc, sc);
sc->sc_chanmap = kmem_zalloc(sizeof(struct vmbus_channel *) *
VMBUS_CHAN_MAX, KM_SLEEP);
if (vmbus_alloc_dma(sc))
goto cleanup;
@ -306,6 +306,8 @@ vmbus_attach(struct vmbus_softc *sc)
cleanup:
vmbus_deinit_interrupts(sc);
vmbus_free_dma(sc);
kmem_free(__UNVOLATILE(sc->sc_chanmap),
sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX);
return -1;
}
@ -326,6 +328,8 @@ vmbus_detach(struct vmbus_softc *sc, int flags)
vmbus_deinit_interrupts(sc);
vmbus_free_dma(sc);
kmem_free(__UNVOLATILE(sc->sc_chanmap),
sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX);
return 0;
}
@ -345,18 +349,18 @@ vmbus_alloc_dma(struct vmbus_softc *sc)
pd = &sc->sc_percpu[cpu_index(ci)];
pd->simp = hyperv_dma_alloc(sc->sc_dmat, &pd->simp_dma,
PAGE_SIZE, PAGE_SIZE, 0, 1, HYPERV_DMA_SLEEPOK);
PAGE_SIZE, PAGE_SIZE, 0, 1);
if (pd->simp == NULL)
return ENOMEM;
pd->siep = hyperv_dma_alloc(sc->sc_dmat, &pd->siep_dma,
PAGE_SIZE, PAGE_SIZE, 0, 1, HYPERV_DMA_SLEEPOK);
PAGE_SIZE, PAGE_SIZE, 0, 1);
if (pd->siep == NULL)
return ENOMEM;
}
sc->sc_events = hyperv_dma_alloc(sc->sc_dmat, &sc->sc_events_dma,
PAGE_SIZE, PAGE_SIZE, 0, 1, HYPERV_DMA_SLEEPOK);
PAGE_SIZE, PAGE_SIZE, 0, 1);
if (sc->sc_events == NULL)
return ENOMEM;
sc->sc_wevents = (u_long *)sc->sc_events;
@ -364,8 +368,7 @@ vmbus_alloc_dma(struct vmbus_softc *sc)
for (i = 0; i < __arraycount(sc->sc_monitor); i++) {
sc->sc_monitor[i] = hyperv_dma_alloc(sc->sc_dmat,
&sc->sc_monitor_dma[i], PAGE_SIZE, PAGE_SIZE, 0, 1,
HYPERV_DMA_SLEEPOK);
&sc->sc_monitor_dma[i], PAGE_SIZE, PAGE_SIZE, 0, 1);
if (sc->sc_monitor[i] == NULL)
return ENOMEM;
}
@ -523,7 +526,7 @@ vmbus_connect(struct vmbus_softc *sc)
for (i = 0; i < __arraycount(versions); i++) {
cmd.chm_ver = versions[i];
rv = vmbus_cmd(sc, &cmd, sizeof(cmd), &rsp, sizeof(rsp),
cold ? HCF_NOSLEEP : HCF_SLEEPOK);
HCF_NOSLEEP);
if (rv) {
DPRINTF("%s: CONNECT failed\n",
device_xname(sc->sc_dev));
@ -549,7 +552,6 @@ static int
vmbus_cmd(struct vmbus_softc *sc, void *cmd, size_t cmdlen, void *rsp,
size_t rsplen, int flags)
{
const int prflags = cold ? PR_NOWAIT : PR_WAITOK;
struct vmbus_msg *msg;
paddr_t pa;
int rv;
@ -560,7 +562,7 @@ vmbus_cmd(struct vmbus_softc *sc, void *cmd, size_t cmdlen, void *rsp,
return EMSGSIZE;
}
msg = pool_cache_get_paddr(sc->sc_msgpool, prflags, &pa);
msg = pool_cache_get_paddr(sc->sc_msgpool, PR_WAITOK, &pa);
if (msg == NULL) {
device_printf(sc->sc_dev, "couldn't get msgpool\n");
return ENOMEM;
@ -588,11 +590,9 @@ vmbus_cmd(struct vmbus_softc *sc, void *cmd, size_t cmdlen, void *rsp,
static int
vmbus_start(struct vmbus_softc *sc, struct vmbus_msg *msg, paddr_t msg_pa)
{
static const int delays[] = {
100, 100, 100, 500, 500, 5000, 5000, 5000
};
const char *wchan = "hvstart";
uint16_t status;
int wait_ms = 1; /* milliseconds */
int i, s;
msg->msg_req.hc_connid = VMBUS_CONNID_MESSAGE;
@ -604,33 +604,45 @@ vmbus_start(struct vmbus_softc *sc, struct vmbus_msg *msg, paddr_t msg_pa)
mutex_exit(&sc->sc_req_lock);
}
for (i = 0; i < __arraycount(delays); i++) {
/*
* In order to cope with transient failures, e.g. insufficient
* resources on host side, we retry the post message Hypercall
* several times. 20 retries seem sufficient.
*/
#define HC_RETRY_MAX 20
#define HC_WAIT_MAX (2 * 1000) /* 2s */
for (i = 0; i < HC_RETRY_MAX; i++) {
status = hyperv_hypercall_post_message(
msg_pa + offsetof(struct vmbus_msg, msg_req));
if (status == HYPERCALL_STATUS_SUCCESS)
break;
return 0;
if (msg->msg_flags & MSGF_NOSLEEP) {
delay(delays[i]);
DELAY(wait_ms * 1000);
s = splnet();
hyperv_intr();
splx(s);
} else
tsleep(wchan, PRIBIO, wchan,
uimax(1, mstohz(delays[i] / 1000)));
}
if (status != HYPERCALL_STATUS_SUCCESS) {
device_printf(sc->sc_dev,
"posting vmbus message failed with %d\n", status);
if (!(msg->msg_flags & MSGF_NOQUEUE)) {
mutex_enter(&sc->sc_req_lock);
TAILQ_REMOVE(&sc->sc_reqs, msg, msg_entry);
mutex_exit(&sc->sc_req_lock);
}
return EIO;
tsleep(wchan, PRIBIO, wchan, uimax(1, mstohz(wait_ms)));
if (wait_ms < HC_WAIT_MAX)
wait_ms *= 2;
}
return 0;
#undef HC_RETRY_MAX
#undef HC_WAIT_MAX
device_printf(sc->sc_dev,
"posting vmbus message failed with %d\n", status);
if (!(msg->msg_flags & MSGF_NOQUEUE)) {
mutex_enter(&sc->sc_req_lock);
TAILQ_REMOVE(&sc->sc_reqs, msg, msg_entry);
mutex_exit(&sc->sc_req_lock);
}
return EIO;
}
static int
@ -664,7 +676,7 @@ vmbus_reply(struct vmbus_softc *sc, struct vmbus_msg *msg)
hyperv_intr();
splx(s);
} else
tsleep(msg, PRIBIO, "hvreply", 1);
tsleep(msg, PRIBIO, "hvreply", uimax(1, mstohz(1)));
}
mutex_enter(&sc->sc_rsp_lock);
@ -705,7 +717,8 @@ vmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *revents,
continue;
pending = atomic_swap_ulong(&revents[row], 0);
chanid_base = row * LONG_BIT;
pending &= ~sc->sc_evtmask[row];
chanid_base = row * VMBUS_EVTFLAG_LEN;
while ((chanid_ofs = ffsl(pending)) != 0) {
chanid_ofs--; /* NOTE: ffs is 1-based */
@ -716,12 +729,12 @@ vmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *revents,
if (chanid == 0)
continue;
ch = vmbus_channel_lookup(sc, chanid);
if (ch == NULL) {
device_printf(sc->sc_dev,
"unhandled event on %d\n", chanid);
ch = sc->sc_chanmap[chanid];
if (__predict_false(ch == NULL)) {
/* Channel is closed. */
continue;
}
__insn_barrier();
if (ch->ch_state != VMBUS_CHANSTATE_OPENED) {
device_printf(sc->sc_dev,
"channel %d is not active\n", chanid);
@ -987,7 +1000,7 @@ vmbus_channel_scan(struct vmbus_softc *sc)
hdr.chm_type = VMBUS_CHANMSG_CHREQUEST;
if (vmbus_cmd(sc, &hdr, sizeof(hdr), &rsp, sizeof(rsp),
HCF_NOREPLY | (cold ? HCF_NOSLEEP : HCF_SLEEPOK))) {
HCF_NOREPLY | HCF_NOSLEEP)) {
DPRINTF("%s: CHREQUEST failed\n", device_xname(sc->sc_dev));
return -1;
}
@ -1013,7 +1026,7 @@ vmbus_channel_alloc(struct vmbus_softc *sc)
ch = kmem_zalloc(sizeof(*ch), KM_SLEEP);
ch->ch_monprm = hyperv_dma_alloc(sc->sc_dmat, &ch->ch_monprm_dma,
sizeof(*ch->ch_monprm), 8, 0, 1, HYPERV_DMA_SLEEPOK);
sizeof(*ch->ch_monprm), 8, 0, 1);
if (ch->ch_monprm == NULL) {
device_printf(sc->sc_dev, "monprm alloc failed\n");
kmem_free(ch, sizeof(*ch));
@ -1022,7 +1035,10 @@ vmbus_channel_alloc(struct vmbus_softc *sc)
ch->ch_refs = 1;
ch->ch_sc = sc;
mutex_init(&ch->ch_event_lock, MUTEX_DEFAULT, IPL_NET);
cv_init(&ch->ch_event_cv, "hvevwait");
mutex_init(&ch->ch_subchannel_lock, MUTEX_DEFAULT, IPL_NET);
cv_init(&ch->ch_subchannel_cv, "hvsubch");
TAILQ_INIT(&ch->ch_subchannels);
ch->ch_state = VMBUS_CHANSTATE_CLOSED;
@ -1043,7 +1059,10 @@ vmbus_channel_free(struct vmbus_channel *ch)
ch->ch_id, ch->ch_refs);
hyperv_dma_free(sc->sc_dmat, &ch->ch_monprm_dma);
mutex_destroy(&ch->ch_event_lock);
cv_destroy(&ch->ch_event_cv);
mutex_destroy(&ch->ch_subchannel_lock);
cv_destroy(&ch->ch_subchannel_cv);
/* XXX ch_evcnt */
if (ch->ch_taskq != NULL)
softint_disestablish(ch->ch_taskq);
@ -1055,7 +1074,7 @@ vmbus_channel_add(struct vmbus_channel *nch)
{
struct vmbus_softc *sc = nch->ch_sc;
struct vmbus_channel *ch;
u_int refs __diagused;
int refs __diagused;
if (nch->ch_id == 0) {
device_printf(sc->sc_dev, "got channel 0 offer, discard\n");
@ -1105,8 +1124,8 @@ vmbus_channel_add(struct vmbus_channel *nch)
mutex_enter(&ch->ch_subchannel_lock);
TAILQ_INSERT_TAIL(&ch->ch_subchannels, nch, ch_subentry);
ch->ch_subchannel_count++;
cv_signal(&ch->ch_subchannel_cv);
mutex_exit(&ch->ch_subchannel_lock);
wakeup(ch);
done:
mutex_enter(&sc->sc_channel_lock);
@ -1133,6 +1152,10 @@ vmbus_channel_cpu_set(struct vmbus_channel *ch, int cpu)
ch->ch_cpuid = cpu;
ch->ch_vcpu = hyperv_get_vcpuid(cpu);
aprint_debug_dev(ch->ch_dev != NULL ? ch->ch_dev : sc->sc_dev,
"channel %u assigned to cpu%u [vcpu%u]\n",
ch->ch_id, ch->ch_cpuid, ch->ch_vcpu);
}
void
@ -1270,7 +1293,7 @@ vmbus_channel_release(struct vmbus_channel *ch)
cmd.chm_chanid = ch->ch_id;
rv = vmbus_cmd(sc, &cmd, sizeof(cmd), NULL, 0,
HCF_NOREPLY | (cold ? HCF_NOSLEEP : HCF_SLEEPOK));
HCF_NOREPLY | HCF_SLEEPOK);
if (rv) {
DPRINTF("%s: CHFREE failed with %d\n", device_xname(sc->sc_dev),
rv);
@ -1279,19 +1302,21 @@ vmbus_channel_release(struct vmbus_channel *ch)
}
struct vmbus_channel **
vmbus_subchannel_get(struct vmbus_channel *prich, int cnt)
vmbus_subchannel_get(struct vmbus_channel *prich, int subchan_cnt)
{
struct vmbus_softc *sc = prich->ch_sc;
struct vmbus_channel **ret, *ch;
int i, s;
KASSERTMSG(cnt > 0, "invalid sub-channel count %d", cnt);
KASSERTMSG(subchan_cnt > 0,
"invalid sub-channel count %d", subchan_cnt);
ret = kmem_zalloc(sizeof(struct vmbus_channel *) * cnt, KM_SLEEP);
ret = kmem_zalloc(sizeof(struct vmbus_channel *) * subchan_cnt,
KM_SLEEP);
mutex_enter(&prich->ch_subchannel_lock);
while (prich->ch_subchannel_count < cnt) {
while (prich->ch_subchannel_count < subchan_cnt) {
if (cold) {
mutex_exit(&prich->ch_subchannel_lock);
delay(1000);
@ -1312,12 +1337,12 @@ vmbus_subchannel_get(struct vmbus_channel *prich, int cnt)
TAILQ_FOREACH(ch, &prich->ch_subchannels, ch_subentry) {
ret[i] = ch; /* XXX inc refs */
if (++i == cnt)
if (++i == subchan_cnt)
break;
}
KASSERTMSG(i == cnt, "invalid subchan count %d, should be %d",
prich->ch_subchannel_count, cnt);
KASSERTMSG(i == subchan_cnt, "invalid subchan count %d, should be %d",
prich->ch_subchannel_count, subchan_cnt);
mutex_exit(&prich->ch_subchannel_lock);
@ -1325,19 +1350,41 @@ vmbus_subchannel_get(struct vmbus_channel *prich, int cnt)
}
void
vmbus_subchannel_put(struct vmbus_channel **subch, int cnt)
vmbus_subchannel_rel(struct vmbus_channel **subch, int cnt)
{
kmem_free(subch, sizeof(struct vmbus_channel *) * cnt);
}
static struct vmbus_channel *
vmbus_channel_lookup(struct vmbus_softc *sc, uint32_t relid)
void
vmbus_subchannel_drain(struct vmbus_channel *prich)
{
struct vmbus_channel *ch;
int s;
mutex_enter(&prich->ch_subchannel_lock);
while (prich->ch_subchannel_count > 0) {
if (cold) {
mutex_exit(&prich->ch_subchannel_lock);
delay(1000);
s = splnet();
hyperv_intr();
splx(s);
mutex_enter(&prich->ch_subchannel_lock);
} else {
cv_wait(&prich->ch_subchannel_cv,
&prich->ch_subchannel_lock);
}
}
mutex_exit(&prich->ch_subchannel_lock);
}
static struct vmbus_channel *
vmbus_channel_lookup(struct vmbus_softc *sc, uint32_t chanid)
{
struct vmbus_channel *ch = NULL;
TAILQ_FOREACH(ch, &sc->sc_channels, ch_entry) {
if (ch->ch_id == relid)
if (ch->ch_id == chanid)
return ch;
}
return NULL;
@ -1352,7 +1399,7 @@ vmbus_channel_ring_create(struct vmbus_channel *ch, uint32_t buflen)
ch->ch_ring_size = 2 * buflen;
/* page aligned memory */
ch->ch_ring = hyperv_dma_alloc(sc->sc_dmat, &ch->ch_ring_dma,
ch->ch_ring_size, PAGE_SIZE, 0, 1, HYPERV_DMA_SLEEPOK);
ch->ch_ring_size, PAGE_SIZE, 0, 1);
if (ch->ch_ring == NULL) {
device_printf(sc->sc_dev,
"failed to allocate channel ring\n");
@ -1414,6 +1461,9 @@ vmbus_channel_open(struct vmbus_channel *ch, size_t buflen, void *udata,
return rv;
}
__insn_barrier();
sc->sc_chanmap[ch->ch_id] = ch;
memset(&cmd, 0, sizeof(cmd));
cmd.chm_hdr.chm_type = VMBUS_CHANMSG_CHOPEN;
cmd.chm_openid = ch->ch_id;
@ -1430,9 +1480,9 @@ vmbus_channel_open(struct vmbus_channel *ch, size_t buflen, void *udata,
ch->ch_ctx = arg;
ch->ch_state = VMBUS_CHANSTATE_OPENED;
rv = vmbus_cmd(sc, &cmd, sizeof(cmd), &rsp, sizeof(rsp),
cold ? HCF_NOSLEEP : HCF_SLEEPOK);
rv = vmbus_cmd(sc, &cmd, sizeof(cmd), &rsp, sizeof(rsp), HCF_NOSLEEP);
if (rv) {
sc->sc_chanmap[ch->ch_id] = NULL;
vmbus_channel_ring_destroy(ch);
DPRINTF("%s: CHOPEN failed with %d\n", device_xname(sc->sc_dev),
rv);
@ -1468,13 +1518,15 @@ vmbus_channel_close_internal(struct vmbus_channel *ch)
struct vmbus_chanmsg_chclose cmd;
int rv;
sc->sc_chanmap[ch->ch_id] = NULL;
memset(&cmd, 0, sizeof(cmd));
cmd.chm_hdr.chm_type = VMBUS_CHANMSG_CHCLOSE;
cmd.chm_chanid = ch->ch_id;
ch->ch_state = VMBUS_CHANSTATE_CLOSING;
rv = vmbus_cmd(sc, &cmd, sizeof(cmd), NULL, 0,
HCF_NOREPLY | (cold ? HCF_NOSLEEP : HCF_SLEEPOK));
HCF_NOREPLY | HCF_NOSLEEP);
if (rv) {
DPRINTF("%s: CHCLOSE failed with %d\n",
device_xname(sc->sc_dev), rv);
@ -1513,7 +1565,7 @@ vmbus_channel_close(struct vmbus_channel *ch)
(void) rv; /* XXX */
vmbus_channel_detach(ch);
}
vmbus_subchannel_put(subch, cnt);
vmbus_subchannel_rel(subch, cnt);
}
return vmbus_channel_close_internal(ch);
@ -1618,6 +1670,13 @@ vmbus_ring_avail(struct vmbus_ring_data *rd, uint32_t *towrite,
*toread = r;
}
static bool
vmbus_ring_is_empty(struct vmbus_ring_data *rd)
{
return rd->rd_ring->br_rindex == rd->rd_ring->br_windex;
}
static int
vmbus_ring_write(struct vmbus_ring_data *wrd, struct iovec *iov, int iov_cnt,
int *needsig)
@ -1886,25 +1945,29 @@ vmbus_ring_unmask(struct vmbus_ring_data *rd)
membar_sync();
}
static void
void
vmbus_channel_pause(struct vmbus_channel *ch)
{
atomic_or_ulong(&ch->ch_sc->sc_evtmask[ch->ch_id / VMBUS_EVTFLAG_LEN],
__BIT(ch->ch_id % VMBUS_EVTFLAG_LEN));
vmbus_ring_mask(&ch->ch_rrd);
}
static uint32_t
uint32_t
vmbus_channel_unpause(struct vmbus_channel *ch)
{
uint32_t avail;
atomic_and_ulong(&ch->ch_sc->sc_evtmask[ch->ch_id / VMBUS_EVTFLAG_LEN],
~__BIT(ch->ch_id % VMBUS_EVTFLAG_LEN));
vmbus_ring_unmask(&ch->ch_rrd);
vmbus_ring_avail(&ch->ch_rrd, NULL, &avail);
return avail;
}
static uint32_t
uint32_t
vmbus_channel_ready(struct vmbus_channel *ch)
{
uint32_t avail;
@ -1914,6 +1977,20 @@ vmbus_channel_ready(struct vmbus_channel *ch)
return avail;
}
bool
vmbus_channel_tx_empty(struct vmbus_channel *ch)
{
return vmbus_ring_is_empty(&ch->ch_wrd);
}
bool
vmbus_channel_rx_empty(struct vmbus_channel *ch)
{
return vmbus_ring_is_empty(&ch->ch_rrd);
}
/* How many PFNs can be referenced by the header */
#define VMBUS_NPFNHDR ((VMBUS_MSG_DSIZE_MAX - \
sizeof(struct vmbus_chanmsg_gpadl_conn)) / sizeof(uint64_t))
@ -1926,10 +2003,6 @@ int
vmbus_handle_alloc(struct vmbus_channel *ch, const struct hyperv_dma *dma,
uint32_t buflen, uint32_t *handle)
{
const int prflags = cold ? PR_NOWAIT : PR_WAITOK;
const int kmemflags = cold ? KM_NOSLEEP : KM_SLEEP;
const int msgflags = cold ? MSGF_NOSLEEP : 0;
const int hcflags = cold ? HCF_NOSLEEP : HCF_SLEEPOK;
struct vmbus_softc *sc = ch->ch_sc;
struct vmbus_chanmsg_gpadl_conn *hdr;
struct vmbus_chanmsg_gpadl_subconn *cmd;
@ -1948,16 +2021,10 @@ vmbus_handle_alloc(struct vmbus_channel *ch, const struct hyperv_dma *dma,
KASSERT((buflen & PAGE_MASK) == 0);
KASSERT(buflen == (uint32_t)dma->map->dm_mapsize);
msg = pool_cache_get_paddr(sc->sc_msgpool, prflags, &pa);
if (msg == NULL)
return ENOMEM;
msg = pool_cache_get_paddr(sc->sc_msgpool, PR_WAITOK, &pa);
/* Prepare array of frame addresses */
frames = kmem_zalloc(total * sizeof(*frames), kmemflags);
if (frames == NULL) {
pool_cache_put_paddr(sc->sc_msgpool, msg, pa);
return ENOMEM;
}
frames = kmem_zalloc(total * sizeof(*frames), KM_SLEEP);
for (i = 0, j = 0; i < dma->map->dm_nsegs && j < total; i++) {
bus_dma_segment_t *seg = &dma->map->dm_segs[i];
bus_addr_t addr = seg->ds_addr;
@ -1977,7 +2044,7 @@ vmbus_handle_alloc(struct vmbus_channel *ch, const struct hyperv_dma *dma,
hdr = (struct vmbus_chanmsg_gpadl_conn *)msg->msg_req.hc_data;
msg->msg_rsp = &rsp;
msg->msg_rsplen = sizeof(rsp);
msg->msg_flags = msgflags;
msg->msg_flags = MSGF_NOSLEEP;
left = total - inhdr;
@ -1985,12 +2052,7 @@ vmbus_handle_alloc(struct vmbus_channel *ch, const struct hyperv_dma *dma,
if (left > 0) {
ncmds = howmany(left, VMBUS_NPFNBODY);
bodylen = ncmds * VMBUS_MSG_DSIZE_MAX;
body = kmem_zalloc(bodylen, kmemflags);
if (body == NULL) {
kmem_free(frames, total * sizeof(*frames));
pool_cache_put_paddr(sc->sc_msgpool, msg, pa);
return ENOMEM;
}
body = kmem_zalloc(bodylen, KM_SLEEP);
}
*handle = atomic_inc_32_nv(&sc->sc_handle);
@ -2035,7 +2097,8 @@ vmbus_handle_alloc(struct vmbus_channel *ch, const struct hyperv_dma *dma,
cmdlen += last * sizeof(uint64_t);
else
cmdlen += VMBUS_NPFNBODY * sizeof(uint64_t);
rv = vmbus_cmd(sc, cmd, cmdlen, NULL, 0, HCF_NOREPLY | hcflags);
rv = vmbus_cmd(sc, cmd, cmdlen, NULL, 0,
HCF_NOREPLY | HCF_NOSLEEP);
if (rv != 0) {
DPRINTF("%s: GPADL_SUBCONN (iteration %d/%d) failed "
"with %d\n", device_xname(sc->sc_dev), i, ncmds,
@ -2075,8 +2138,7 @@ vmbus_handle_free(struct vmbus_channel *ch, uint32_t handle)
cmd.chm_chanid = ch->ch_id;
cmd.chm_gpadl = handle;
rv = vmbus_cmd(sc, &cmd, sizeof(cmd), &rsp, sizeof(rsp),
cold ? HCF_NOSLEEP : HCF_SLEEPOK);
rv = vmbus_cmd(sc, &cmd, sizeof(cmd), &rsp, sizeof(rsp), HCF_NOSLEEP);
if (rv) {
DPRINTF("%s: GPADL_DISCONN failed with %d\n",
device_xname(sc->sc_dev), rv);

View File

@ -1,4 +1,4 @@
/* $NetBSD: vmbusvar.h,v 1.6 2020/07/14 00:45:53 yamaguchi Exp $ */
/* $NetBSD: vmbusvar.h,v 1.7 2022/05/20 13:55:17 nonaka Exp $ */
/* $OpenBSD: hypervvar.h,v 1.13 2017/06/23 19:05:42 mikeb Exp $ */
/*
@ -121,6 +121,9 @@ struct vmbus_channel {
struct evcnt ch_evcnt;
void *ch_taskq;
kmutex_t ch_event_lock;
kcondvar_t ch_event_cv;
uint32_t ch_flags;
#define CHF_BATCHED __BIT(0)
#define CHF_MONITOR __BIT(1)
@ -135,6 +138,7 @@ struct vmbus_channel {
TAILQ_ENTRY(vmbus_channel) ch_entry;
kmutex_t ch_subchannel_lock;
kcondvar_t ch_subchannel_cv;
struct vmbus_channels ch_subchannels;
u_int ch_subchannel_count;
TAILQ_ENTRY(vmbus_channel) ch_subentry;
@ -175,6 +179,8 @@ struct vmbus_softc {
u_long *sc_wevents; /* Write events */
u_long *sc_revents; /* Read events */
struct vmbus_channel * volatile *sc_chanmap;
volatile u_long sc_evtmask[VMBUS_EVTFLAGS_MAX];
struct vmbus_mnf *sc_monitor[2];
struct vmbus_percpu_data sc_percpu[MAXCPUS];
@ -286,9 +292,15 @@ int vmbus_channel_recv(struct vmbus_channel *, void *, uint32_t, uint32_t *,
void vmbus_channel_cpu_set(struct vmbus_channel *, int);
void vmbus_channel_cpu_rr(struct vmbus_channel *);
bool vmbus_channel_is_revoked(struct vmbus_channel *);
bool vmbus_channel_tx_empty(struct vmbus_channel *);
bool vmbus_channel_rx_empty(struct vmbus_channel *);
void vmbus_channel_pause(struct vmbus_channel *);
uint32_t vmbus_channel_unpause(struct vmbus_channel *);
uint32_t vmbus_channel_ready(struct vmbus_channel *);
struct vmbus_channel **
vmbus_subchannel_get(struct vmbus_channel *, int);
void vmbus_subchannel_put(struct vmbus_channel **, int);
void vmbus_subchannel_rel(struct vmbus_channel **, int);
void vmbus_subchannel_drain(struct vmbus_channel *);
#endif /* _VMBUSVAR_H_ */