Author: sephe
Date: Fri Oct 21 08:02:05 2016
New Revision: 307712
URL: https://svnweb.freebsd.org/changeset/base/307712

Log:
  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

Modified:
  head/sys/dev/hyperv/netvsc/hv_net_vsc.h
  head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
  head/sys/dev/hyperv/netvsc/hv_rndis_filter.c
  head/sys/dev/hyperv/netvsc/if_hnvar.h
  head/sys/dev/hyperv/netvsc/ndis.h
  head/sys/net/rndis.h

Modified: head/sys/dev/hyperv/netvsc/hv_net_vsc.h
==============================================================================
--- head/sys/dev/hyperv/netvsc/hv_net_vsc.h     Fri Oct 21 07:46:35 2016        
(r307711)
+++ head/sys/dev/hyperv/netvsc/hv_net_vsc.h     Fri Oct 21 08:02:05 2016        
(r307712)
@@ -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
  */

Modified: head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
==============================================================================
--- head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c  Fri Oct 21 07:46:35 
2016        (r307711)
+++ head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c  Fri Oct 21 08:02:05 
2016        (r307712)
@@ -335,6 +335,8 @@ static void hn_destroy_tx_data(struct hn
 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_cha
 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
        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 
        }
 
        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 *s
                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

Modified: head/sys/dev/hyperv/netvsc/hv_rndis_filter.c
==============================================================================
--- head/sys/dev/hyperv/netvsc/hv_rndis_filter.c        Fri Oct 21 07:46:35 
2016        (r307711)
+++ head/sys/dev/hyperv/netvsc/hv_rndis_filter.c        Fri Oct 21 08:02:05 
2016        (r307712)
@@ -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_
                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:

Modified: head/sys/dev/hyperv/netvsc/if_hnvar.h
==============================================================================
--- head/sys/dev/hyperv/netvsc/if_hnvar.h       Fri Oct 21 07:46:35 2016        
(r307711)
+++ head/sys/dev/hyperv/netvsc/if_hnvar.h       Fri Oct 21 08:02:05 2016        
(r307712)
@@ -139,6 +139,7 @@ int         hn_rxpkt(struct hn_rx_ring *rxr, co
                    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;
 

Modified: head/sys/dev/hyperv/netvsc/ndis.h
==============================================================================
--- head/sys/dev/hyperv/netvsc/ndis.h   Fri Oct 21 07:46:35 2016        
(r307711)
+++ head/sys/dev/hyperv/netvsc/ndis.h   Fri Oct 21 08:02:05 2016        
(r307712)
@@ -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

Modified: head/sys/net/rndis.h
==============================================================================
--- head/sys/net/rndis.h        Fri Oct 21 07:46:35 2016        (r307711)
+++ head/sys/net/rndis.h        Fri Oct 21 08:02:05 2016        (r307712)
@@ -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
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to