From 6cb2b7828ab4ecfe8c1b7a3dab02ca0547344193 Mon Sep 17 00:00:00 2001 Message-Id: <6cb2b7828ab4ecfe8c1b7a3dab02ca0547344193.1365434988.git.minovotn@redhat.com> From: Xiao Wang Date: Thu, 7 Feb 2013 05:23:30 +0100 Subject: [PATCH 1/2] e1000: fix link down handling with auto negotiation RH-Author: Xiao Wang Message-id: <1360214611-11202-1-git-send-email-jasowang@redhat.com> Patchwork-id: 48249 O-Subject: [RHEL6.4z/RHEL6.5 PATCH V2 1/2] e1000: fix link down handling with auto negotiation Bugzilla: 907716 RH-Acked-by: Amos Kong RH-Acked-by: Stefan Hajnoczi RH-Acked-by: Michael S. Tsirkin From: Michael S. Tsirkin Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=907716 Brew Build: https://brewweb.devel.redhat.com/taskinfo?taskID=5367557 Upstream: post Test status: Test by me and QE. Fixes a couple of regression bugs introduced by b9d03e352cb6b31a66545763f6a1e20c9abf0c2c and related to auto-negotiation: - Auto-negotiation currently sets link up even if it was forced down from the monitor. - If Auto-negotiation was in progress during migration, link will never come up. As a fix, don't touch NC link_down field at all, instead add code on receive path to check guest link status. Signed-off-by: Michael S. Tsirkin Signed-off-by: Jason Wang --- hw/e1000.c | 41 +++++++++++++++++++++++++++++++++++------ 1 files changed, 35 insertions(+), 6 deletions(-) Signed-off-by: Michal Novotny --- hw/e1000.c | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/hw/e1000.c b/hw/e1000.c index f1665a4..32f63d9 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -167,7 +167,6 @@ static void set_phy_ctrl(E1000State *s, int index, uint16_t val) { if ((val & MII_CR_AUTO_NEG_EN) && (val & MII_CR_RESTART_AUTO_NEG)) { - s->nic->nc.link_down = true; e1000_link_down(s); s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; DBGOUT(PHY, "Start link auto negotiation\n"); @@ -179,8 +178,9 @@ static void e1000_autoneg_timer(void *opaque) { E1000State *s = opaque; - s->nic->nc.link_down = false; - e1000_link_up(s); + if (!s->nic->nc.link_down) { + e1000_link_up(s); + } s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; DBGOUT(PHY, "Auto negotiation is completed\n"); } @@ -751,7 +751,8 @@ e1000_can_receive(VLANClientState *nc) { E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; - return (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1); + return (s->mac_reg[STATUS] & E1000_STATUS_LU) && + (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1); } static ssize_t @@ -768,8 +769,13 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) size_t desc_size; uint8_t min_buf[MIN_BUF_SIZE]; - if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) + if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) { return -1; + } + + if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) { + return -1; + } /* Pad to minimum Ethernet frame length */ if (size < sizeof(min_buf)) { @@ -1062,13 +1068,35 @@ static bool is_version_1(void *opaque, int version_id) return version_id == 1; } +static void e1000_pre_save(void *opaque) +{ + E1000State *s = opaque; + /* + * If link is down and auto-negotiation is ongoing, complete + * auto-negotiation immediately. This allows is to look at + * MII_SR_AUTONEG_COMPLETE to infer link status on load. + */ + if (s->nic->nc.link_down && + s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN && + s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG) { + s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + } +} + static int e1000_post_load(void *opaque, int version_id) { E1000State *s = opaque; /* nc.link_down can't be migrated, so infer link_down according - * to link status bit in mac_reg[STATUS] */ + * to link status bit in mac_reg[STATUS]. + * Alternatively, restart link negotiation if it was in progress. */ s->nic->nc.link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0; + if (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN && + s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG && + !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + s->nic->nc.link_down = false; + qemu_mod_timer(s->autoneg_timer, qemu_get_clock(vm_clock) + 500000000); + } return 0; } @@ -1078,6 +1106,7 @@ static const VMStateDescription vmstate_e1000 = { .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, + .pre_save = e1000_pre_save, .post_load = e1000_post_load, .fields = (VMStateField []) { VMSTATE_PCI_DEVICE(dev, E1000State), -- 1.7.11.7