NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/52103: vioif(4) writes to VIRTIO_NET_S_LINK_UP that is read-only bit
>Number: 52103
>Category: kern
>Synopsis: vioif(4) writes to VIRTIO_NET_S_LINK_UP that is read-only bit
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Mar 23 02:45:00 +0000 2017
>Originator: Shoichi Yamaguchi
>Release: -current
>Organization:
Internet Initiative Japan Inc.
>Environment:
>Description:
According to Virtio PCI Card Specification v0.9.5
(http://ozlabs.org/~rusty/virtio-spec/.), VIRTIO_NET_S_LINK_UP bit is
defined as read-only.But, vioif(4) writes values in there to control
link status when the interface up and down. By this problem, the hung
up is happened on Google Compute Engine if MSI-X is disable.
>How-To-Repeat:
Up or down the interface.
- ifconfig vioif0 up
- ifconfig vioif0 down
>Fix:
patch 1: Use virtio_reset() for a pre-init state to realize link
down instead of write 0 to VIRTIO_NET_S_LINK_UP
patch 2: Add the handler for config change interrupt to synchronize
with hypervisors link state
I referred the FreeBSD implementation for the two patches
===== patch 1 =====
diff --git a/sys/dev/pci/if_vioif.c b/sys/dev/pci/if_vioif.c
index e7fdbf2..68b9923 100644
--- a/sys/dev/pci/if_vioif.c
+++ b/sys/dev/pci/if_vioif.c
@@ -267,7 +267,6 @@ static int vioif_tx_vq_done_locked(struct virtqueue *);
static void vioif_tx_drain(struct vioif_softc *);
/* other control */
-static int vioif_updown(struct vioif_softc *, bool);
static int vioif_ctrl_rx(struct vioif_softc *, int, bool);
static int vioif_set_promisc(struct vioif_softc *, bool);
static int vioif_set_allmulti(struct vioif_softc *, bool);
@@ -719,12 +718,19 @@ static int
vioif_init(struct ifnet *ifp)
{
struct vioif_softc *sc = ifp->if_softc;
+ struct virtio_softc *vsc = sc->sc_virtio;
vioif_stop(ifp, 0);
- if (!sc->sc_deferred_init_done) {
- struct virtio_softc *vsc = sc->sc_virtio;
+ virtio_reinit_start(vsc);
+ virtio_negotiate_features(vsc, vsc->sc_features);
+ virtio_start_vq_intr(vsc, &sc->sc_vq[VQ_RX]);
+ virtio_stop_vq_intr(vsc, &sc->sc_vq[VQ_TX]);
+ if (vsc->sc_nvqs >= 3)
+ virtio_start_vq_intr(vsc, &sc->sc_vq[VQ_CTRL]);
+ virtio_reinit_end(vsc);
+ if (!sc->sc_deferred_init_done) {
sc->sc_deferred_init_done = 1;
if (vsc->sc_nvqs == 3)
vioif_deferred_init(sc->sc_dev);
@@ -735,7 +741,6 @@ vioif_init(struct ifnet *ifp)
vioif_populate_rx_mbufs(sc);
- vioif_updown(sc, true);
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
vioif_rx_filter(sc);
@@ -756,6 +761,12 @@ vioif_stop(struct ifnet *ifp, int disable)
VIOIF_RX_UNLOCK(sc);
VIOIF_TX_UNLOCK(sc);
+ /* disable interrupts */
+ virtio_stop_vq_intr(vsc, &sc->sc_vq[VQ_RX]);
+ virtio_stop_vq_intr(vsc, &sc->sc_vq[VQ_TX]);
+ if (vsc->sc_nvqs >= 3)
+ virtio_stop_vq_intr(vsc, &sc->sc_vq[VQ_CTRL]);
+
/* only way to stop I/O and DMA is resetting... */
virtio_reset(vsc);
vioif_rx_deq(sc);
@@ -764,15 +775,6 @@ vioif_stop(struct ifnet *ifp, int disable)
if (disable)
vioif_rx_drain(sc);
-
- virtio_reinit_start(vsc);
- virtio_negotiate_features(vsc, vsc->sc_features);
- virtio_start_vq_intr(vsc, &sc->sc_vq[VQ_RX]);
- virtio_stop_vq_intr(vsc, &sc->sc_vq[VQ_TX]);
- if (vsc->sc_nvqs >= 3)
- virtio_start_vq_intr(vsc, &sc->sc_vq[VQ_CTRL]);
- virtio_reinit_end(vsc);
- vioif_updown(sc, false);
}
static void
@@ -1502,20 +1504,6 @@ set:
return r;
}
-/* change link status */
-static int
-vioif_updown(struct vioif_softc *sc, bool isup)
-{
- struct virtio_softc *vsc = sc->sc_virtio;
-
- if (!(vsc->sc_features & VIRTIO_NET_F_STATUS))
- return ENODEV;
- virtio_write_device_config_1(vsc,
- VIRTIO_NET_CONFIG_STATUS,
- isup?VIRTIO_NET_S_LINK_UP:0);
- return 0;
-}
-
MODULE(MODULE_CLASS_DRIVER, if_vioif, "virtio");
#ifdef _MODULE
===== patch 1 =====
===== patch 2 =====
diff --git a/sys/dev/pci/if_vioif.c b/sys/dev/pci/if_vioif.c
index 68b9923..f9a80e1 100644
--- a/sys/dev/pci/if_vioif.c
+++ b/sys/dev/pci/if_vioif.c
@@ -190,6 +190,7 @@ struct vioif_softc {
uint8_t sc_mac[ETHER_ADDR_LEN];
struct ethercom sc_ethercom;
short sc_deferred_init_done;
+ bool sc_link_active;
/* bus_dmamem */
bus_dma_segment_t sc_hdr_segs[1];
@@ -218,6 +219,7 @@ struct vioif_softc {
bus_dmamap_t sc_ctrl_tbl_mc_dmamap;
void *sc_rx_softint;
+ void *sc_ctl_softint;
enum {
FREE, INUSE, DONE
@@ -267,12 +269,16 @@ static int vioif_tx_vq_done_locked(struct virtqueue *);
static void vioif_tx_drain(struct vioif_softc *);
/* other control */
+static bool vioif_is_link_up(struct vioif_softc *);
+static void vioif_update_link_status(struct vioif_softc *);
static int vioif_ctrl_rx(struct vioif_softc *, int, bool);
static int vioif_set_promisc(struct vioif_softc *, bool);
static int vioif_set_allmulti(struct vioif_softc *, bool);
static int vioif_set_rx_filter(struct vioif_softc *);
static int vioif_rx_filter(struct vioif_softc *);
static int vioif_ctrl_vq_done(struct virtqueue *);
+static int vioif_config_change(struct virtio_softc *);
+static void vioif_ctl_softint(void *);
CFATTACH_DECL_NEW(vioif, sizeof(struct vioif_softc),
vioif_match, vioif_attach, NULL, NULL);
@@ -523,11 +529,12 @@ vioif_attach(device_t parent, device_t self, void *aux)
sc->sc_dev = self;
sc->sc_virtio = vsc;
+ sc->sc_link_active = false;
vsc->sc_child = self;
vsc->sc_ipl = IPL_NET;
vsc->sc_vqs = &sc->sc_vq[0];
- vsc->sc_config_change = NULL;
+ vsc->sc_config_change = vioif_config_change;
vsc->sc_intrhand = virtio_vq_intr;
vsc->sc_flags = 0;
@@ -651,7 +658,13 @@ skip:
#endif
sc->sc_rx_softint = softint_establish(flags, vioif_rx_softint, sc);
if (sc->sc_rx_softint == NULL) {
- aprint_error_dev(self, "cannot establish softint\n");
+ aprint_error_dev(self, "cannot establish rx softint\n");
+ goto err;
+ }
+
+ sc->sc_ctl_softint = softint_establish(flags, vioif_ctl_softint, sc);
+ if (sc->sc_ctl_softint == NULL) {
+ aprint_error_dev(self, "cannot establish ctl softint\n");
goto err;
}
@@ -741,6 +754,7 @@ vioif_init(struct ifnet *ifp)
vioif_populate_rx_mbufs(sc);
+ vioif_update_link_status(sc);
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
vioif_rx_filter(sc);
@@ -772,6 +786,7 @@ vioif_stop(struct ifnet *ifp, int disable)
vioif_rx_deq(sc);
vioif_tx_drain(sc);
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ sc->sc_link_active = false;
if (disable)
vioif_rx_drain(sc);
@@ -788,7 +803,8 @@ vioif_start(struct ifnet *ifp)
VIOIF_TX_LOCK(sc);
- if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
+ if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING ||
+ !sc->sc_link_active)
goto out;
if (sc->sc_stopping)
@@ -1504,6 +1520,82 @@ set:
return r;
}
+static bool
+vioif_is_link_up(struct vioif_softc *sc)
+{
+ struct virtio_softc *vsc = sc->sc_virtio;
+ uint16_t status;
+
+ if (vsc->sc_features & VIRTIO_NET_F_STATUS)
+ status = virtio_read_device_config_2(vsc,
+ VIRTIO_NET_CONFIG_STATUS);
+ else
+ status = VIRTIO_NET_S_LINK_UP;
+
+ return ((status & VIRTIO_NET_S_LINK_UP) != 0);
+}
+
+/* change link status */
+static void
+vioif_update_link_status(struct vioif_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+ bool active, changed;
+ int link;
+
+ active = vioif_is_link_up(sc);
+ changed = false;
+
+ VIOIF_TX_LOCK(sc);
+ if (active) {
+ if (!sc->sc_link_active)
+ changed = true;
+
+ link = LINK_STATE_UP;
+ sc->sc_link_active = true;
+ } else {
+ if (sc->sc_link_active)
+ changed = true;
+
+ link = LINK_STATE_DOWN;
+ sc->sc_link_active = false;
+ }
+ VIOIF_TX_UNLOCK(sc);
+
+ if (changed)
+ if_link_state_change(ifp, link);
+}
+
+static int
+vioif_config_change(struct virtio_softc *vsc)
+{
+ struct vioif_softc *sc = device_private(vsc->sc_child);
+
+#ifdef VIOIF_SOFTINT_INTR
+ struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+#endif
+
+#ifdef VIOIF_SOFTINT_INTR
+ KASSERT(!cpu_intr_p());
+ vioif_update_link_status(sc);
+ vioif_start(ifp);
+#else
+ softint_schedule(sc->sc_ctl_softint);
+#endif
+
+ return 0;
+}
+
+static void
+vioif_ctl_softint(void *arg)
+{
+ struct vioif_softc *sc = arg;
+ struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+
+ vioif_update_link_status(sc);
+ vioif_start(ifp);
+}
+
MODULE(MODULE_CLASS_DRIVER, if_vioif, "virtio");
#ifdef _MODULE
diff --git a/sys/dev/pci/virtio.c b/sys/dev/pci/virtio.c
index 8efda6b..063b3e0 100644
--- a/sys/dev/pci/virtio.c
+++ b/sys/dev/pci/virtio.c
@@ -653,10 +653,11 @@ static int
virtio_msix_config_intr(void *arg)
{
struct virtio_softc *sc = arg;
+ int r = 0;
- /* TODO: handle events */
- aprint_debug_dev(sc->sc_dev, "%s\n", __func__);
- return 1;
+ if (sc->sc_config_change != NULL)
+ r = (sc->sc_config_change)(sc);
+ return r;
}
static void
Home |
Main Index |
Thread Index |
Old Index