summaryrefslogtreecommitdiffstats
path: root/sys/dev/hyperv/netvsc
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/hyperv/netvsc')
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.c217
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.h7
-rw-r--r--sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c354
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis_filter.c186
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis_filter.h2
-rw-r--r--sys/dev/hyperv/netvsc/if_hnvar.h16
6 files changed, 434 insertions, 348 deletions
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.c b/sys/dev/hyperv/netvsc/hv_net_vsc.c
index b66fe41..a64eb62 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.c
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.c
@@ -57,11 +57,10 @@ MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver");
/*
* Forward declarations
*/
-static int hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc);
-static int hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *);
-static int hv_nv_destroy_send_buffer(struct hn_softc *sc);
-static int hv_nv_destroy_rx_buffer(struct hn_softc *sc);
-static int hv_nv_connect_to_vsp(struct hn_softc *sc, int mtu);
+static int hn_nvs_conn_chim(struct hn_softc *sc);
+static int hn_nvs_conn_rxbuf(struct hn_softc *);
+static int hn_nvs_disconn_chim(struct hn_softc *sc);
+static int hn_nvs_disconn_rxbuf(struct hn_softc *sc);
static void hn_nvs_sent_none(struct hn_send_ctx *sndc,
struct hn_softc *, struct vmbus_channel *chan,
const void *, int);
@@ -103,7 +102,7 @@ hn_chim_alloc(struct hn_softc *sc)
return (ret);
}
-const void *
+static const void *
hn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact,
void *req, int reqlen, size_t *resplen0, uint32_t type)
{
@@ -154,14 +153,8 @@ hn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen)
req, reqlen, &hn_send_ctx_none));
}
-/*
- * Net VSC initialize receive buffer with net VSP
- *
- * Net VSP: Network virtual services client, also known as the
- * Hyper-V extensible switch and the synthetic data path.
- */
static int
-hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc)
+hn_nvs_conn_rxbuf(struct hn_softc *sc)
{
struct vmbus_xact *xact = NULL;
struct hn_nvs_rxbuf_conn *conn;
@@ -188,7 +181,7 @@ hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc)
error = vmbus_chan_gpadl_connect(sc->hn_prichan,
sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl);
if (error) {
- if_printf(sc->hn_ifp, "rxbuf gpadl connect failed: %d\n",
+ if_printf(sc->hn_ifp, "rxbuf gpadl conn failed: %d\n",
error);
goto cleanup;
}
@@ -212,7 +205,7 @@ hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc)
resp = hn_nvs_xact_execute(sc, xact, conn, sizeof(*conn), &resp_len,
HN_NVS_TYPE_RXBUF_CONNRESP);
if (resp == NULL) {
- if_printf(sc->hn_ifp, "exec rxbuf conn failed\n");
+ if_printf(sc->hn_ifp, "exec nvs rxbuf conn failed\n");
error = EIO;
goto cleanup;
}
@@ -222,7 +215,7 @@ hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc)
xact = NULL;
if (status != HN_NVS_STATUS_OK) {
- if_printf(sc->hn_ifp, "rxbuf conn failed: %x\n", status);
+ if_printf(sc->hn_ifp, "nvs rxbuf conn failed: %x\n", status);
error = EIO;
goto cleanup;
}
@@ -233,15 +226,12 @@ hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc)
cleanup:
if (xact != NULL)
vmbus_xact_put(xact);
- hv_nv_destroy_rx_buffer(sc);
+ hn_nvs_disconn_rxbuf(sc);
return (error);
}
-/*
- * Net VSC initialize send buffer with net VSP
- */
static int
-hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc)
+hn_nvs_conn_chim(struct hn_softc *sc)
{
struct vmbus_xact *xact = NULL;
struct hn_nvs_chim_conn *chim;
@@ -261,8 +251,7 @@ hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc)
sc->hn_chim_dma.hv_paddr, NETVSC_SEND_BUFFER_SIZE,
&sc->hn_chim_gpadl);
if (error) {
- if_printf(sc->hn_ifp, "chimney sending buffer gpadl "
- "connect failed: %d\n", error);
+ if_printf(sc->hn_ifp, "chim gpadl conn failed: %d\n", error);
goto cleanup;
}
@@ -285,7 +274,7 @@ hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc)
resp = hn_nvs_xact_execute(sc, xact, chim, sizeof(*chim), &resp_len,
HN_NVS_TYPE_CHIM_CONNRESP);
if (resp == NULL) {
- if_printf(sc->hn_ifp, "exec chim conn failed\n");
+ if_printf(sc->hn_ifp, "exec nvs chim conn failed\n");
error = EIO;
goto cleanup;
}
@@ -296,14 +285,14 @@ hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc)
xact = NULL;
if (status != HN_NVS_STATUS_OK) {
- if_printf(sc->hn_ifp, "chim conn failed: %x\n", status);
+ if_printf(sc->hn_ifp, "nvs chim conn failed: %x\n", status);
error = EIO;
goto cleanup;
}
if (sectsz == 0) {
if_printf(sc->hn_ifp, "zero chimney sending buffer "
"section size\n");
- return 0;
+ return (0);
}
sc->hn_chim_szmax = sectsz;
@@ -327,22 +316,19 @@ hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc)
if_printf(sc->hn_ifp, "chimney sending buffer %d/%d\n",
sc->hn_chim_szmax, sc->hn_chim_cnt);
}
- return 0;
+ return (0);
cleanup:
if (xact != NULL)
vmbus_xact_put(xact);
- hv_nv_destroy_send_buffer(sc);
+ hn_nvs_disconn_chim(sc);
return (error);
}
-/*
- * Net VSC destroy receive buffer
- */
static int
-hv_nv_destroy_rx_buffer(struct hn_softc *sc)
+hn_nvs_disconn_rxbuf(struct hn_softc *sc)
{
- int ret = 0;
+ int error;
if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) {
struct hn_nvs_rxbuf_disconn disconn;
@@ -355,38 +341,35 @@ hv_nv_destroy_rx_buffer(struct hn_softc *sc)
disconn.nvs_sig = HN_NVS_RXBUF_SIG;
/* NOTE: No response. */
- ret = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
- if (ret != 0) {
+ error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
+ if (error) {
if_printf(sc->hn_ifp,
- "send rxbuf disconn failed: %d\n", ret);
- return (ret);
+ "send nvs rxbuf disconn failed: %d\n", error);
+ return (error);
}
sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED;
}
-
+
if (sc->hn_rxbuf_gpadl != 0) {
/*
* Disconnect RXBUF from primary channel.
*/
- ret = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
+ error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
sc->hn_rxbuf_gpadl);
- if (ret != 0) {
+ if (error) {
if_printf(sc->hn_ifp,
- "rxbuf disconn failed: %d\n", ret);
- return (ret);
+ "rxbuf gpadl disconn failed: %d\n", error);
+ return (error);
}
sc->hn_rxbuf_gpadl = 0;
}
- return (ret);
+ return (0);
}
-/*
- * Net VSC destroy send buffer
- */
static int
-hv_nv_destroy_send_buffer(struct hn_softc *sc)
+hn_nvs_disconn_chim(struct hn_softc *sc)
{
- int ret = 0;
+ int error;
if (sc->hn_flags & HN_FLAG_CHIM_CONNECTED) {
struct hn_nvs_chim_disconn disconn;
@@ -399,25 +382,25 @@ hv_nv_destroy_send_buffer(struct hn_softc *sc)
disconn.nvs_sig = HN_NVS_CHIM_SIG;
/* NOTE: No response. */
- ret = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
- if (ret != 0) {
+ error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
+ if (error) {
if_printf(sc->hn_ifp,
- "send chim disconn failed: %d\n", ret);
- return (ret);
+ "send nvs chim disconn failed: %d\n", error);
+ return (error);
}
sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED;
}
-
+
if (sc->hn_chim_gpadl != 0) {
/*
* Disconnect chimney sending buffer from primary channel.
*/
- ret = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
+ error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
sc->hn_chim_gpadl);
- if (ret != 0) {
+ if (error) {
if_printf(sc->hn_ifp,
- "chim disconn failed: %d\n", ret);
- return (ret);
+ "chim gpadl disconn failed: %d\n", error);
+ return (error);
}
sc->hn_chim_gpadl = 0;
}
@@ -426,8 +409,7 @@ hv_nv_destroy_send_buffer(struct hn_softc *sc)
free(sc->hn_chim_bmap, M_NETVSC);
sc->hn_chim_bmap = NULL;
}
-
- return (ret);
+ return (0);
}
static int
@@ -538,38 +520,48 @@ hn_nvs_init(struct hn_softc *sc)
return (ENXIO);
}
-static int
-hv_nv_connect_to_vsp(struct hn_softc *sc, int mtu)
+int
+hn_nvs_attach(struct hn_softc *sc, int mtu)
{
- int ret;
+ int error;
/*
* Initialize NVS.
*/
- ret = hn_nvs_init(sc);
- if (ret != 0)
- return (ret);
+ error = hn_nvs_init(sc);
+ if (error)
+ return (error);
if (sc->hn_nvs_ver >= HN_NVS_VERSION_2) {
/*
* Configure NDIS before initializing it.
*/
- ret = hn_nvs_conf_ndis(sc, mtu);
- if (ret != 0)
- return (ret);
+ error = hn_nvs_conf_ndis(sc, mtu);
+ if (error)
+ return (error);
}
/*
* Initialize NDIS.
*/
- ret = hn_nvs_init_ndis(sc);
- if (ret != 0)
- return (ret);
+ error = hn_nvs_init_ndis(sc);
+ if (error)
+ return (error);
- ret = hv_nv_init_rx_buffer_with_net_vsp(sc);
- if (ret == 0)
- ret = hv_nv_init_send_buffer_with_net_vsp(sc);
- return (ret);
+ /*
+ * Connect RXBUF.
+ */
+ error = hn_nvs_conn_rxbuf(sc);
+ if (error)
+ return (error);
+
+ /*
+ * Connect chimney sending buffer.
+ */
+ error = hn_nvs_conn_chim(sc);
+ if (error)
+ return (error);
+ return (0);
}
/*
@@ -578,23 +570,8 @@ hv_nv_connect_to_vsp(struct hn_softc *sc, int mtu)
static void
hv_nv_disconnect_from_vsp(struct hn_softc *sc)
{
- hv_nv_destroy_rx_buffer(sc);
- hv_nv_destroy_send_buffer(sc);
-}
-
-/*
- * Net VSC on device add
- *
- * Callback when the device belonging to this driver is added
- */
-int
-hv_nv_on_device_add(struct hn_softc *sc, int mtu)
-{
-
- /*
- * Connect with the NetVsp
- */
- return (hv_nv_connect_to_vsp(sc, mtu));
+ hn_nvs_disconn_rxbuf(sc);
+ hn_nvs_disconn_chim(sc);
}
/*
@@ -605,11 +582,6 @@ hv_nv_on_device_remove(struct hn_softc *sc)
{
hv_nv_disconnect_from_vsp(sc);
-
- /* Now, we can close the channel safely */
-
- vmbus_chan_close(sc->hn_prichan);
-
return (0);
}
@@ -676,3 +648,54 @@ hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype,
return (ret);
}
+
+int
+hn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch0)
+{
+ struct vmbus_xact *xact;
+ struct hn_nvs_subch_req *req;
+ const struct hn_nvs_subch_resp *resp;
+ int error, nsubch_req;
+ uint32_t nsubch;
+ size_t resp_len;
+
+ nsubch_req = *nsubch0;
+ KASSERT(nsubch_req > 0, ("invalid # of sub-channels %d", nsubch_req));
+
+ xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
+ if (xact == NULL) {
+ if_printf(sc->hn_ifp, "no xact for nvs subch alloc\n");
+ return (ENXIO);
+ }
+ req = vmbus_xact_req_data(xact);
+ req->nvs_type = HN_NVS_TYPE_SUBCH_REQ;
+ req->nvs_op = HN_NVS_SUBCH_OP_ALLOC;
+ req->nvs_nsubch = nsubch_req;
+
+ resp_len = sizeof(*resp);
+ resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len,
+ HN_NVS_TYPE_SUBCH_RESP);
+ if (resp == NULL) {
+ if_printf(sc->hn_ifp, "exec nvs subch alloc failed\n");
+ error = EIO;
+ goto done;
+ }
+ if (resp->nvs_status != HN_NVS_STATUS_OK) {
+ if_printf(sc->hn_ifp, "nvs subch alloc failed: %x\n",
+ resp->nvs_status);
+ error = EIO;
+ goto done;
+ }
+
+ nsubch = resp->nvs_nsubch;
+ if (nsubch > nsubch_req) {
+ if_printf(sc->hn_ifp, "%u subchans are allocated, "
+ "requested %d\n", nsubch, nsubch_req);
+ nsubch = nsubch_req;
+ }
+ *nsubch0 = nsubch;
+ error = 0;
+done:
+ vmbus_xact_put(xact);
+ return (error);
+}
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h
index 077cd10..6a55117 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.h
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h
@@ -99,11 +99,6 @@ struct vmbus_channel;
#define NETVSC_DEVICE_RING_BUFFER_SIZE (128 * PAGE_SIZE)
#define NETVSC_PACKET_MAXPAGE 32
-typedef struct {
- uint8_t mac_addr[ETHER_ADDR_LEN];
- uint32_t link_state;
-} netvsc_device_info;
-
#define HN_XACT_REQ_PGCNT 2
#define HN_XACT_RESP_PGCNT 2
#define HN_XACT_REQ_SIZE (HN_XACT_REQ_PGCNT * PAGE_SIZE)
@@ -261,7 +256,7 @@ extern int hv_promisc_mode;
struct hn_send_ctx;
void netvsc_linkstatus_callback(struct hn_softc *sc, uint32_t status);
-int hv_nv_on_device_add(struct hn_softc *sc, int mtu);
+int hn_nvs_attach(struct hn_softc *sc, int mtu);
int hv_nv_on_device_remove(struct hn_softc *sc);
int hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype,
struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt);
diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
index d2afa0f..4d816c3 100644
--- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
+++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
@@ -345,8 +345,12 @@ static int hn_create_rx_data(struct hn_softc *sc, int);
static void hn_destroy_rx_data(struct hn_softc *sc);
static void hn_set_chim_size(struct hn_softc *, int);
static int hn_chan_attach(struct hn_softc *, struct vmbus_channel *);
+static void hn_chan_detach(struct hn_softc *, struct vmbus_channel *);
static int hn_attach_subchans(struct hn_softc *);
+static void hn_detach_allchans(struct hn_softc *);
static void hn_chan_callback(struct vmbus_channel *chan, void *xrxr);
+static void hn_set_ring_inuse(struct hn_softc *, int);
+static int hn_synth_attach(struct hn_softc *, int);
static void hn_nvs_handle_notify(struct hn_softc *sc,
const struct vmbus_chanpkt_hdr *pkt);
@@ -364,6 +368,14 @@ static void hn_xmit_txeof(struct hn_tx_ring *);
static void hn_xmit_taskfunc(void *, int);
static void hn_xmit_txeof_taskfunc(void *, int);
+static const uint8_t hn_rss_key_default[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
+ 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+ 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+ 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+ 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+ 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
+};
+
#if __FreeBSD_version >= 1100099
static void
hn_set_lro_lenlim(struct hn_softc *sc, int lenlim)
@@ -440,7 +452,8 @@ netvsc_attach(device_t dev)
{
struct sysctl_oid_list *child;
struct sysctl_ctx_list *ctx;
- netvsc_device_info device_info;
+ uint8_t eaddr[ETHER_ADDR_LEN];
+ uint32_t link_status;
hn_softc_t *sc;
int unit = device_get_unit(dev);
struct ifnet *ifp = NULL;
@@ -519,9 +532,17 @@ netvsc_attach(device_t dev)
goto failed;
/*
- * Associate the first TX/RX ring w/ the primary channel.
+ * Create transaction context for NVS and RNDIS transactions.
*/
- error = hn_chan_attach(sc, sc->hn_prichan);
+ sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
+ HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
+ if (sc->hn_xact == NULL)
+ goto failed;
+
+ /*
+ * Attach the synthetic parts, i.e. NVS and RNDIS.
+ */
+ error = hn_synth_attach(sc, ETHERMTU);
if (error)
goto failed;
@@ -559,34 +580,6 @@ netvsc_attach(device_t dev)
IFCAP_LRO;
ifp->if_hwassist = sc->hn_tx_ring[0].hn_csum_assist | CSUM_TSO;
- sc->hn_xact = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
- HN_XACT_REQ_SIZE, HN_XACT_RESP_SIZE, 0);
- if (sc->hn_xact == NULL)
- goto failed;
-
- error = hv_rf_on_device_add(sc, &device_info, &ring_cnt, ETHERMTU);
- if (error)
- goto failed;
- KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_inuse,
- ("invalid channel count %d, should be less than %d",
- ring_cnt, sc->hn_rx_ring_inuse));
-
- /*
- * Set the # of TX/RX rings that could be used according to
- * the # of channels that host offered.
- */
- if (sc->hn_tx_ring_inuse > ring_cnt)
- sc->hn_tx_ring_inuse = ring_cnt;
- sc->hn_rx_ring_inuse = ring_cnt;
- device_printf(dev, "%d TX ring, %d RX ring\n",
- sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
-
- if (sc->hn_rx_ring_inuse > 1) {
- error = hn_attach_subchans(sc);
- if (error)
- goto failed;
- }
-
#if __FreeBSD_version >= 1100099
if (sc->hn_rx_ring_inuse > 1) {
/*
@@ -597,9 +590,11 @@ netvsc_attach(device_t dev)
}
#endif
- if (device_info.link_state == NDIS_MEDIA_STATE_CONNECTED) {
+ error = hn_rndis_get_linkstatus(sc, &link_status);
+ if (error)
+ goto failed;
+ if (link_status == NDIS_MEDIA_STATE_CONNECTED)
sc->hn_carrier = 1;
- }
#if __FreeBSD_version >= 1100045
tso_maxlen = hn_tso_maxlen;
@@ -612,7 +607,10 @@ netvsc_attach(device_t dev)
(ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN);
#endif
- ether_ifattach(ifp, device_info.mac_addr);
+ error = hn_rndis_get_eaddr(sc, eaddr);
+ if (error)
+ goto failed;
+ ether_ifattach(ifp, eaddr);
#if __FreeBSD_version >= 1100045
if_printf(ifp, "TSO: %u/%u/%u\n", ifp->if_hw_tsomax,
@@ -663,6 +661,7 @@ netvsc_detach(device_t dev)
*/
hv_rf_on_device_remove(sc);
+ hn_detach_allchans(sc);
hn_stop_tx_tasks(sc);
@@ -1503,8 +1502,7 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
#ifdef INET
struct ifaddr *ifa = (struct ifaddr *)data;
#endif
- netvsc_device_info device_info;
- int mask, error = 0, ring_cnt;
+ int mask, error = 0;
int retry_cnt = 500;
switch(cmd) {
@@ -1575,43 +1573,16 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
}
- /* Wait for subchannels to be destroyed */
- vmbus_subchan_drain(sc->hn_prichan);
-
- sc->hn_rx_ring[0].hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
- sc->hn_tx_ring[0].hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
- hn_chan_attach(sc, sc->hn_prichan); /* XXX check error */
-
- ring_cnt = sc->hn_rx_ring_inuse;
- error = hv_rf_on_device_add(sc, &device_info, &ring_cnt,
- ifr->ifr_mtu);
- if (error) {
- NV_LOCK(sc);
- sc->temp_unusable = FALSE;
- NV_UNLOCK(sc);
- break;
- }
- /* # of channels can _not_ be changed */
- KASSERT(sc->hn_rx_ring_inuse == ring_cnt,
- ("RX ring count %d and channel count %u mismatch",
- sc->hn_rx_ring_cnt, ring_cnt));
- if (sc->hn_rx_ring_inuse > 1) {
- int r;
+ /*
+ * Detach all of the channels.
+ */
+ hn_detach_allchans(sc);
- /*
- * Skip the rings on primary channel; they are
- * handled by the hv_rf_on_device_add() above.
- */
- for (r = 1; r < sc->hn_rx_ring_cnt; ++r) {
- sc->hn_rx_ring[r].hn_rx_flags &=
- ~HN_RX_FLAG_ATTACHED;
- }
- for (r = 1; r < sc->hn_tx_ring_cnt; ++r) {
- sc->hn_tx_ring[r].hn_tx_flags &=
- ~HN_TX_FLAG_ATTACHED;
- }
- hn_attach_subchans(sc); /* XXX check error */
- }
+ /*
+ * Attach the synthetic parts, i.e. NVS and RNDIS.
+ * XXX check error.
+ */
+ hn_synth_attach(sc, ifr->ifr_mtu);
if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
hn_set_chim_size(sc, sc->hn_chim_szmax);
@@ -3018,6 +2989,42 @@ hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
return (error);
}
+static void
+hn_chan_detach(struct hn_softc *sc, struct vmbus_channel *chan)
+{
+ struct hn_rx_ring *rxr;
+ int idx;
+
+ idx = vmbus_chan_subidx(chan);
+
+ /*
+ * Link this channel to RX/TX ring.
+ */
+ KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
+ ("invalid channel index %d, should > 0 && < %d",
+ idx, sc->hn_rx_ring_inuse));
+ rxr = &sc->hn_rx_ring[idx];
+ KASSERT((rxr->hn_rx_flags & HN_RX_FLAG_ATTACHED),
+ ("RX ring %d is not attached", idx));
+ rxr->hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
+
+ if (idx < sc->hn_tx_ring_inuse) {
+ struct hn_tx_ring *txr = &sc->hn_tx_ring[idx];
+
+ KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED),
+ ("TX ring %d is not attached attached", idx));
+ txr->hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
+ }
+
+ /*
+ * Close this channel.
+ *
+ * NOTE:
+ * Channel closing does _not_ destroy the target channel.
+ */
+ vmbus_chan_close(chan);
+}
+
static int
hn_attach_subchans(struct hn_softc *sc)
{
@@ -3025,17 +3032,16 @@ hn_attach_subchans(struct hn_softc *sc)
int subchan_cnt = sc->hn_rx_ring_inuse - 1;
int i, error = 0;
- /* Wait for sub-channels setup to complete. */
- subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
+ if (subchan_cnt == 0)
+ return (0);
/* Attach the sub-channels. */
+ subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
for (i = 0; i < subchan_cnt; ++i) {
error = hn_chan_attach(sc, subchans[i]);
if (error)
break;
}
-
- /* Release the sub-channels */
vmbus_subchan_rel(subchans, subchan_cnt);
if (error) {
@@ -3050,6 +3056,202 @@ hn_attach_subchans(struct hn_softc *sc)
}
static void
+hn_detach_allchans(struct hn_softc *sc)
+{
+ struct vmbus_channel **subchans;
+ int subchan_cnt = sc->hn_rx_ring_inuse - 1;
+ int i;
+
+ if (subchan_cnt == 0)
+ goto back;
+
+ /* Detach the sub-channels. */
+ subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
+ for (i = 0; i < subchan_cnt; ++i)
+ hn_chan_detach(sc, subchans[i]);
+ vmbus_subchan_rel(subchans, subchan_cnt);
+
+back:
+ /*
+ * Detach the primary channel, _after_ all sub-channels
+ * are detached.
+ */
+ hn_chan_detach(sc, sc->hn_prichan);
+
+ /* Wait for sub-channels to be destroyed, if any. */
+ vmbus_subchan_drain(sc->hn_prichan);
+
+#ifdef INVARIANTS
+ for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
+ KASSERT((sc->hn_rx_ring[i].hn_rx_flags &
+ HN_RX_FLAG_ATTACHED) == 0,
+ ("%dth RX ring is still attached", i));
+ }
+ for (i = 0; i < sc->hn_tx_ring_cnt; ++i) {
+ KASSERT((sc->hn_tx_ring[i].hn_tx_flags &
+ HN_TX_FLAG_ATTACHED) == 0,
+ ("%dth TX ring is still attached", i));
+ }
+#endif
+}
+
+static int
+hn_synth_alloc_subchans(struct hn_softc *sc, int *nsubch)
+{
+ struct vmbus_channel **subchans;
+ int nchan, rxr_cnt, error;
+
+ nchan = *nsubch + 1;
+ if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30 || nchan == 1) {
+ /*
+ * Either RSS is not supported, or multiple RX/TX rings
+ * are not requested.
+ */
+ *nsubch = 0;
+ return (0);
+ }
+
+ /*
+ * Get RSS capabilities, e.g. # of RX rings, and # of indirect
+ * table entries.
+ */
+ error = hn_rndis_get_rsscaps(sc, &rxr_cnt);
+ if (error) {
+ /* No RSS; this is benign. */
+ *nsubch = 0;
+ return (0);
+ }
+ if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
+ rxr_cnt, nchan);
+
+ if (nchan > rxr_cnt)
+ nchan = rxr_cnt;
+ if (nchan == 1) {
+ if_printf(sc->hn_ifp, "only 1 channel is supported, no vRSS\n");
+ *nsubch = 0;
+ return (0);
+ }
+
+ /*
+ * Allocate sub-channels from NVS.
+ */
+ *nsubch = nchan - 1;
+ error = hn_nvs_alloc_subchans(sc, nsubch);
+ if (error || *nsubch == 0) {
+ /* Failed to allocate sub-channels. */
+ *nsubch = 0;
+ return (0);
+ }
+
+ /*
+ * Wait for all sub-channels to become ready before moving on.
+ */
+ subchans = vmbus_subchan_get(sc->hn_prichan, *nsubch);
+ vmbus_subchan_rel(subchans, *nsubch);
+ return (0);
+}
+
+static int
+hn_synth_attach(struct hn_softc *sc, int mtu)
+{
+ struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
+ int error, nsubch, nchan, i;
+
+ /*
+ * Attach the primary channel _before_ attaching NVS and RNDIS.
+ */
+ error = hn_chan_attach(sc, sc->hn_prichan);
+ if (error)
+ return (error);
+
+ /*
+ * Attach NVS.
+ */
+ error = hn_nvs_attach(sc, mtu);
+ if (error)
+ return (error);
+
+ /*
+ * Attach RNDIS _after_ NVS is attached.
+ */
+ error = hn_rndis_attach(sc);
+ if (error)
+ return (error);
+
+ /*
+ * Allocate sub-channels for multi-TX/RX rings.
+ *
+ * NOTE:
+ * The # of RX rings that can be used is equivalent to the # of
+ * channels to be requested.
+ */
+ nsubch = sc->hn_rx_ring_cnt - 1;
+ error = hn_synth_alloc_subchans(sc, &nsubch);
+ if (error)
+ return (error);
+
+ nchan = nsubch + 1;
+ if (nchan == 1) {
+ /* Only the primary channel can be used; done */
+ goto back;
+ }
+
+ /*
+ * Configure RSS key and indirect table _after_ all sub-channels
+ * are allocated.
+ */
+
+ /* Setup default RSS key. */
+ memcpy(rss->rss_key, hn_rss_key_default, sizeof(rss->rss_key));
+
+ /* Setup default RSS indirect table. */
+ /* TODO: Take ndis_rss_caps.ndis_nind into account. */
+ for (i = 0; i < NDIS_HASH_INDCNT; ++i)
+ rss->rss_ind[i] = i % nchan;
+
+ error = hn_rndis_conf_rss(sc);
+ if (error) {
+ /*
+ * Failed to configure RSS key or indirect table; only
+ * the primary channel can be used.
+ */
+ nchan = 1;
+ }
+back:
+ /*
+ * Set the # of TX/RX rings that could be used according to
+ * the # of channels that NVS offered.
+ */
+ hn_set_ring_inuse(sc, nchan);
+
+ /*
+ * Attach the sub-channels, if any.
+ */
+ error = hn_attach_subchans(sc);
+ if (error)
+ return (error);
+ return (0);
+}
+
+static void
+hn_set_ring_inuse(struct hn_softc *sc, int ring_cnt)
+{
+ KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_cnt,
+ ("invalid ring count %d", ring_cnt));
+
+ if (sc->hn_tx_ring_cnt > ring_cnt)
+ sc->hn_tx_ring_inuse = ring_cnt;
+ else
+ sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt;
+ sc->hn_rx_ring_inuse = ring_cnt;
+
+ if (bootverbose) {
+ if_printf(sc->hn_ifp, "%d TX ring, %d RX ring\n",
+ sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
+ }
+}
+
+static void
hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
{
const struct hn_nvs_hdr *hdr;
diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.c b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
index 1279e9f..ff7a84c 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis_filter.c
+++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
@@ -75,18 +75,12 @@ static void hv_rf_receive_indicate_status(struct hn_softc *sc,
const void *data, int dlen);
static void hv_rf_receive_data(struct hn_rx_ring *rxr,
const void *data, int dlen);
-static int hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr);
-static int hv_rf_query_device_link_status(struct hn_softc *sc,
- uint32_t *link_status);
-static int hv_rf_init_device(struct hn_softc *sc);
static int hn_rndis_query(struct hn_softc *sc, uint32_t oid,
const void *idata, size_t idlen, void *odata, size_t *odlen0);
static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data,
size_t dlen);
static int hn_rndis_conf_offload(struct hn_softc *sc);
-static int hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt);
-static int hn_rndis_conf_rss(struct hn_softc *sc, int nchan);
static __inline uint32_t
hn_rndis_rid(struct hn_softc *sc)
@@ -480,11 +474,8 @@ hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
}
}
-/*
- * RNDIS filter query device MAC address
- */
-static int
-hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr)
+int
+hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr)
{
size_t eaddr_len;
int error;
@@ -501,11 +492,8 @@ hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr)
return (0);
}
-/*
- * RNDIS filter query device link status
- */
-static int
-hv_rf_query_device_link_status(struct hn_softc *sc, uint32_t *link_status)
+int
+hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status)
{
size_t size;
int error;
@@ -522,14 +510,6 @@ hv_rf_query_device_link_status(struct hn_softc *sc, uint32_t *link_status)
return (0);
}
-static uint8_t netvsc_hash_key[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
- 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
- 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
- 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
- 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
- 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
-};
-
static const void *
hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
struct hn_send_ctx *sndc, size_t *comp_len)
@@ -721,7 +701,7 @@ done:
return (error);
}
-static int
+int
hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt)
{
struct ndis_rss_caps in, caps;
@@ -856,12 +836,12 @@ hn_rndis_conf_offload(struct hn_softc *sc)
return (error);
}
-static int
-hn_rndis_conf_rss(struct hn_softc *sc, int nchan)
+int
+hn_rndis_conf_rss(struct hn_softc *sc)
{
struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
struct ndis_rss_params *prm = &rss->rss_params;
- int i, error;
+ int error;
/*
* Only NDIS 6.30+ is supported.
@@ -869,7 +849,12 @@ hn_rndis_conf_rss(struct hn_softc *sc, int nchan)
KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30,
("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
- memset(rss, 0, sizeof(*rss));
+ /*
+ * NOTE:
+ * DO NOT whack rss_key and rss_ind, which are setup by the caller.
+ */
+ memset(prm, 0, sizeof(*prm));
+
prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS;
prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
prm->ndis_hdr.ndis_size = sizeof(*rss);
@@ -884,14 +869,6 @@ hn_rndis_conf_rss(struct hn_softc *sc, int nchan)
prm->ndis_keyoffset =
__offsetof(struct ndis_rssprm_toeplitz, rss_key[0]);
- /* Setup RSS key */
- memcpy(rss->rss_key, netvsc_hash_key, sizeof(rss->rss_key));
-
- /* Setup RSS indirect table */
- /* TODO: Take ndis_rss_caps.ndis_nind into account */
- for (i = 0; i < NDIS_HASH_INDCNT; ++i)
- rss->rss_ind[i] = i % nchan;
-
error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
rss, sizeof(*rss));
if (error) {
@@ -922,11 +899,8 @@ hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
return (error);
}
-/*
- * RNDIS filter init device
- */
static int
-hv_rf_init_device(struct hn_softc *sc)
+hn_rndis_init(struct hn_softc *sc)
{
struct rndis_init_req *req;
const struct rndis_init_comp *comp;
@@ -1007,136 +981,24 @@ hv_rf_halt_device(struct hn_softc *sc)
return (0);
}
-/*
- * RNDIS filter on device add
- */
int
-hv_rf_on_device_add(struct hn_softc *sc, void *additl_info,
- int *nchan0, int mtu)
+hn_rndis_attach(struct hn_softc *sc)
{
- int ret;
- netvsc_device_info *dev_info = (netvsc_device_info *)additl_info;
- device_t dev = sc->hn_dev;
- struct hn_nvs_subch_req *req;
- const struct hn_nvs_subch_resp *resp;
- size_t resp_len;
- struct vmbus_xact *xact = NULL;
- uint32_t status, nsubch;
- int nchan = *nchan0;
- int rxr_cnt;
+ int error;
/*
- * Let the inner driver handle this first to create the netvsc channel
- * NOTE! Once the channel is created, we may get a receive callback
- * (hv_rf_on_receive()) before this call is completed.
- * Note: Earlier code used a function pointer here.
+ * Initialize RNDIS.
*/
- ret = hv_nv_on_device_add(sc, mtu);
- if (ret != 0)
- return (ret);
+ error = hn_rndis_init(sc);
+ if (error)
+ return (error);
/*
- * Initialize the rndis device
+ * Configure NDIS offload settings.
+ * XXX no offloading, if error happened?
*/
-
- /* Send the rndis initialization message */
- ret = hv_rf_init_device(sc);
- if (ret != 0) {
- /*
- * TODO: If rndis init failed, we will need to shut down
- * the channel
- */
- }
-
- /* Get the mac address */
- ret = hv_rf_query_device_mac(sc, dev_info->mac_addr);
- if (ret != 0) {
- /* TODO: shut down rndis device and the channel */
- }
-
- /* Configure NDIS offload settings */
hn_rndis_conf_offload(sc);
-
- hv_rf_query_device_link_status(sc, &dev_info->link_state);
-
- if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30 || nchan == 1) {
- /*
- * Either RSS is not supported, or multiple RX/TX rings
- * are not requested.
- */
- *nchan0 = 1;
- return (0);
- }
-
- /*
- * Get RSS capabilities, e.g. # of RX rings, and # of indirect
- * table entries.
- */
- ret = hn_rndis_get_rsscaps(sc, &rxr_cnt);
- if (ret) {
- /* No RSS; this is benign. */
- *nchan0 = 1;
- return (0);
- }
- if (nchan > rxr_cnt)
- nchan = rxr_cnt;
- if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
- rxr_cnt, nchan);
-
- if (nchan == 1) {
- device_printf(dev, "only 1 channel is supported, no vRSS\n");
- goto out;
- }
-
- /*
- * Ask NVS to allocate sub-channels.
- */
- xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
- if (xact == NULL) {
- if_printf(sc->hn_ifp, "no xact for nvs subch req\n");
- ret = ENXIO;
- goto out;
- }
- req = vmbus_xact_req_data(xact);
- req->nvs_type = HN_NVS_TYPE_SUBCH_REQ;
- req->nvs_op = HN_NVS_SUBCH_OP_ALLOC;
- req->nvs_nsubch = nchan - 1;
-
- resp_len = sizeof(*resp);
- resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len,
- HN_NVS_TYPE_SUBCH_RESP);
- if (resp == NULL) {
- if_printf(sc->hn_ifp, "exec subch failed\n");
- ret = EIO;
- goto out;
- }
-
- status = resp->nvs_status;
- nsubch = resp->nvs_nsubch;
- vmbus_xact_put(xact);
- xact = NULL;
-
- if (status != HN_NVS_STATUS_OK) {
- if_printf(sc->hn_ifp, "subch req failed: %x\n", status);
- ret = EIO;
- goto out;
- }
- if (nsubch > nchan - 1) {
- if_printf(sc->hn_ifp, "%u subchans are allocated, requested %u\n",
- nsubch, nchan - 1);
- nsubch = nchan - 1;
- }
- nchan = nsubch + 1;
-
- ret = hn_rndis_conf_rss(sc, nchan);
- if (ret != 0)
- *nchan0 = 1;
- else
- *nchan0 = nchan;
-out:
- if (xact != NULL)
- vmbus_xact_put(xact);
- return (ret);
+ return (0);
}
/*
diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.h b/sys/dev/hyperv/netvsc/hv_rndis_filter.h
index 18dd460..f389ced 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis_filter.h
+++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.h
@@ -43,8 +43,6 @@ struct hn_rx_ring;
void hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
const void *data, int dlen);
void hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr);
-int hv_rf_on_device_add(struct hn_softc *sc, void *additl_info, int *nchan,
- int mtu);
int hv_rf_on_device_remove(struct hn_softc *sc);
int hv_rf_on_open(struct hn_softc *sc);
int hv_rf_on_close(struct hn_softc *sc);
diff --git a/sys/dev/hyperv/netvsc/if_hnvar.h b/sys/dev/hyperv/netvsc/if_hnvar.h
index 8be1aa3..23626ee 100644
--- a/sys/dev/hyperv/netvsc/if_hnvar.h
+++ b/sys/dev/hyperv/netvsc/if_hnvar.h
@@ -114,16 +114,22 @@ hn_nvs_send_sglist(struct vmbus_channel *chan, struct vmbus_gpa sg[], int sglen,
struct vmbus_xact;
struct rndis_packet_msg;
-const void *hn_nvs_xact_execute(struct hn_softc *sc,
- struct vmbus_xact *xact, void *req, int reqlen,
- size_t *resp_len, uint32_t type);
-void hn_nvs_sent_xact(struct hn_send_ctx *sndc, struct hn_softc *sc,
- struct vmbus_channel *chan, const void *data, int dlen);
uint32_t hn_chim_alloc(struct hn_softc *sc);
void hn_chim_free(struct hn_softc *sc, uint32_t chim_idx);
+int hn_rndis_attach(struct hn_softc *sc);
+int hn_rndis_conf_rss(struct hn_softc *sc);
void *hn_rndis_pktinfo_append(struct rndis_packet_msg *,
size_t pktsize, size_t pi_dlen, uint32_t pi_type);
+int hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt);
+int hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr);
+int hn_rndis_get_linkstatus(struct hn_softc *sc,
+ uint32_t *link_status);
+
+int hn_nvs_attach(struct hn_softc *sc, int mtu);
+int hn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch);
+void hn_nvs_sent_xact(struct hn_send_ctx *sndc, struct hn_softc *sc,
+ struct vmbus_channel *chan, const void *data, int dlen);
int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
const struct hn_recvinfo *info);
OpenPOWER on IntegriCloud