aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorSepherosa Ziehau <sephe@FreeBSD.org>2016-10-21 08:02:05 +0000
committerSepherosa Ziehau <sephe@FreeBSD.org>2016-10-21 08:02:05 +0000
commit970ead008d48e1e27e6da6abb143e31bbabbf752 (patch)
tree47a6dd450ba0114c09afdb5bcb819de304c770bb /sys
parent31f05efd89ded4d2fe78d31a0b2be685ed1717f3 (diff)
downloadsrc-970ead008d48e1e27e6da6abb143e31bbabbf752.tar.gz
src-970ead008d48e1e27e6da6abb143e31bbabbf752.zip
hyperv/hn: Add network change support.
Currently the network change is simulated by link status changes. MFC after: 1 week Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8295
Notes
Notes: svn path=/head/; revision=307712
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.h7
-rw-r--r--sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c75
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis_filter.c16
-rw-r--r--sys/dev/hyperv/netvsc/if_hnvar.h1
-rw-r--r--sys/dev/hyperv/netvsc/ndis.h4
-rw-r--r--sys/net/rndis.h4
6 files changed, 94 insertions, 13 deletions
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h
index a07fa7f1a944..06a6dfe8c98b 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.h
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h
@@ -207,7 +207,6 @@ struct hn_softc {
struct ifnet *hn_ifp;
struct ifmedia hn_media;
device_t hn_dev;
- int hn_carrier;
int hn_if_flags;
struct sx hn_lock;
struct vmbus_channel *hn_prichan;
@@ -236,6 +235,9 @@ struct hn_softc {
struct taskqueue *hn_mgmt_taskq;
struct taskqueue *hn_mgmt_taskq0;
struct task hn_link_task;
+ struct task hn_netchg_init;
+ struct timeout_task hn_netchg_status;
+ uint32_t hn_link_flags; /* HN_LINK_FLAG_ */
uint32_t hn_caps; /* HN_CAP_ */
uint32_t hn_flags; /* HN_FLAG_ */
@@ -271,6 +273,9 @@ struct hn_softc {
#define HN_CAP_TSO6 0x0100
#define HN_CAP_HASHVAL 0x0200
+#define HN_LINK_FLAG_LINKUP 0x0001
+#define HN_LINK_FLAG_NETCHG 0x0002
+
/*
* Externs
*/
diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
index 46688b08d7e0..79b5ec6a814f 100644
--- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
+++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
@@ -335,6 +335,8 @@ static void hn_destroy_tx_data(struct hn_softc *);
static void hn_start_taskfunc(void *, int);
static void hn_start_txeof_taskfunc(void *, int);
static void hn_link_taskfunc(void *, int);
+static void hn_netchg_init_taskfunc(void *, int);
+static void hn_netchg_status_taskfunc(void *, int);
static void hn_suspend_mgmt_taskfunc(void *, int);
static int hn_encap(struct hn_tx_ring *, struct hn_txdesc *, struct mbuf **);
static int hn_create_rx_data(struct hn_softc *sc, int);
@@ -360,6 +362,7 @@ static void hn_rx_drain(struct vmbus_channel *);
static void hn_tx_resume(struct hn_softc *, int);
static void hn_tx_ring_qflush(struct hn_tx_ring *);
static int netvsc_detach(device_t dev);
+static void hn_link_status(struct hn_softc *);
static void hn_nvs_handle_notify(struct hn_softc *sc,
const struct vmbus_chanpkt_hdr *pkt);
@@ -482,7 +485,7 @@ hn_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
ifmr->ifm_status = IFM_AVALID;
ifmr->ifm_active = IFM_ETHER;
- if (!sc->hn_carrier) {
+ if ((sc->hn_link_flags & HN_LINK_FLAG_LINKUP) == 0) {
ifmr->ifm_active |= IFM_NONE;
return;
}
@@ -563,6 +566,9 @@ netvsc_attach(device_t dev)
taskqueue_start_threads(&sc->hn_mgmt_taskq0, 1, PI_NET, "%s mgmt",
device_get_nameunit(dev));
TASK_INIT(&sc->hn_link_task, 0, hn_link_taskfunc, sc);
+ TASK_INIT(&sc->hn_netchg_init, 0, hn_netchg_init_taskfunc, sc);
+ TIMEOUT_TASK_INIT(sc->hn_mgmt_taskq0, &sc->hn_netchg_status, 0,
+ hn_netchg_status_taskfunc, sc);
/*
* Allocate ifnet and setup its name earlier, so that if_printf
@@ -808,10 +814,8 @@ netvsc_shutdown(device_t dev)
}
static void
-hn_link_taskfunc(void *xsc, int pending __unused)
+hn_link_status(struct hn_softc *sc)
{
- struct hn_softc *sc = xsc;
- struct ifnet *ifp = sc->hn_ifp;
uint32_t link_status;
int error;
@@ -822,11 +826,51 @@ hn_link_taskfunc(void *xsc, int pending __unused)
}
if (link_status == NDIS_MEDIA_STATE_CONNECTED)
- sc->hn_carrier = 1;
+ sc->hn_link_flags |= HN_LINK_FLAG_LINKUP;
else
- sc->hn_carrier = 0;
- if_link_state_change(ifp,
- sc->hn_carrier ? LINK_STATE_UP : LINK_STATE_DOWN);
+ sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
+ if_link_state_change(sc->hn_ifp,
+ (sc->hn_link_flags & HN_LINK_FLAG_LINKUP) ?
+ LINK_STATE_UP : LINK_STATE_DOWN);
+}
+
+static void
+hn_link_taskfunc(void *xsc, int pending __unused)
+{
+ struct hn_softc *sc = xsc;
+
+ if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG)
+ return;
+ hn_link_status(sc);
+}
+
+static void
+hn_netchg_init_taskfunc(void *xsc, int pending __unused)
+{
+ struct hn_softc *sc = xsc;
+
+ /* Prevent any link status checks from running. */
+ sc->hn_link_flags |= HN_LINK_FLAG_NETCHG;
+
+ /*
+ * Fake up a [link down --> link up] state change; 5 seconds
+ * delay is used, which closely simulates miibus reaction
+ * upon link down event.
+ */
+ sc->hn_link_flags &= ~HN_LINK_FLAG_LINKUP;
+ if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
+ taskqueue_enqueue_timeout(sc->hn_mgmt_taskq0,
+ &sc->hn_netchg_status, 5 * hz);
+}
+
+static void
+hn_netchg_status_taskfunc(void *xsc, int pending __unused)
+{
+ struct hn_softc *sc = xsc;
+
+ /* Re-allow link status checks. */
+ sc->hn_link_flags &= ~HN_LINK_FLAG_NETCHG;
+ hn_link_status(sc);
}
void
@@ -837,6 +881,14 @@ hn_link_status_update(struct hn_softc *sc)
taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task);
}
+void
+hn_network_change(struct hn_softc *sc)
+{
+
+ if (sc->hn_mgmt_taskq != NULL)
+ taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_netchg_init);
+}
+
static __inline int
hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd,
struct mbuf **m_head, bus_dma_segment_t *segs, int *nsegs)
@@ -3719,6 +3771,8 @@ hn_suspend_mgmt(struct hn_softc *sc)
/*
* Make sure that all pending management tasks are completed.
*/
+ taskqueue_drain(sc->hn_mgmt_taskq0, &sc->hn_netchg_init);
+ taskqueue_drain_timeout(sc->hn_mgmt_taskq0, &sc->hn_netchg_status);
taskqueue_drain_all(sc->hn_mgmt_taskq0);
}
@@ -3796,10 +3850,11 @@ hn_resume_mgmt(struct hn_softc *sc)
{
/*
- * Kick off link status check.
+ * Kick off network change detection, which will
+ * do link status check too.
*/
sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
- hn_link_status_update(sc);
+ hn_network_change(sc);
}
static void
diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.c b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
index dde3acb4886a..14446c6c5401 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis_filter.c
+++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
@@ -158,6 +158,7 @@ static void
hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
{
const struct rndis_status_msg *msg;
+ int ofs;
if (dlen < sizeof(*msg)) {
if_printf(sc->hn_ifp, "invalid RNDIS status\n");
@@ -176,8 +177,19 @@ hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
break;
case RNDIS_STATUS_NETWORK_CHANGE:
- /* TODO */
- if_printf(sc->hn_ifp, "network changed\n");
+ ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
+ if (dlen < ofs + msg->rm_stbuflen ||
+ msg->rm_stbuflen < sizeof(uint32_t)) {
+ if_printf(sc->hn_ifp, "network changed\n");
+ } else {
+ uint32_t change;
+
+ memcpy(&change, ((const uint8_t *)msg) + ofs,
+ sizeof(change));
+ if_printf(sc->hn_ifp, "network changed, change %u\n",
+ change);
+ }
+ hn_network_change(sc);
break;
default:
diff --git a/sys/dev/hyperv/netvsc/if_hnvar.h b/sys/dev/hyperv/netvsc/if_hnvar.h
index 517d2815908b..4afe1f14e124 100644
--- a/sys/dev/hyperv/netvsc/if_hnvar.h
+++ b/sys/dev/hyperv/netvsc/if_hnvar.h
@@ -139,6 +139,7 @@ int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
const struct hn_recvinfo *info);
void hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr);
void hn_link_status_update(struct hn_softc *sc);
+void hn_network_change(struct hn_softc *sc);
extern struct hn_send_ctx hn_send_ctx_none;
diff --git a/sys/dev/hyperv/netvsc/ndis.h b/sys/dev/hyperv/netvsc/ndis.h
index fed262dddf67..5c741c6db330 100644
--- a/sys/dev/hyperv/netvsc/ndis.h
+++ b/sys/dev/hyperv/netvsc/ndis.h
@@ -32,6 +32,10 @@
#define NDIS_MEDIA_STATE_CONNECTED 0
#define NDIS_MEDIA_STATE_DISCONNECTED 1
+#define NDIS_NETCHANGE_TYPE_POSSIBLE 1
+#define NDIS_NETCHANGE_TYPE_DEFINITE 2
+#define NDIS_NETCHANGE_TYPE_FROMMEDIA 3
+
#define NDIS_OFFLOAD_SET_NOCHG 0
#define NDIS_OFFLOAD_SET_ON 1
#define NDIS_OFFLOAD_SET_OFF 2
diff --git a/sys/net/rndis.h b/sys/net/rndis.h
index 9da76bc08d24..95edb1c5d7e4 100644
--- a/sys/net/rndis.h
+++ b/sys/net/rndis.h
@@ -320,6 +320,10 @@ struct rndis_status_msg {
/* rndis_diag_info */
};
+/* stbuf offset from the beginning of rndis_status_msg. */
+#define RNDIS_STBUFOFFSET_ABS(ofs) \
+ ((ofs) + __offsetof(struct rndis_status_msg, rm_status))
+
/*
* Immediately after rndis_status_msg.rm_stbufoffset, if a control
* message is malformatted, or a packet message contains inappropriate