shill: avoid virtio_net race condition

With the virtnet driver, it's possible for the kernel's IPv6 code
to transmit frames before virtnet_probe completes. This seems to confuse
the driver, making the device unable to transmit. To make use of the
device, one then has to unload and reload (rmmod+modprobe) the driver.
This makes difficult to run shill in a VM. That means that a patch
to enable shill as the default CM won't make it through the commit queue.

Work around the virtnet+IPv6 problem by sleeping before bringing
up the virtual ethernet device. Empirically, it takes about 7ms
from the time shill gets the notification about the virtnet device
being available, until the time when virtnet_probe completes.
We sleep 2 seconds (2000ms) to leave lots of slack.

Collateral change: declare some Ethernet methods as virtual.
They're already virtual (by virtual of their base class declarations),
but declaring them virtual in Ethernet makes this explicit.

BUG=chromium-os:29494
TEST=trybots, manual testing (see below)

Trybots: ran on 10 trybots, with 20 iterations of VMTest
each. (Using https://gerrit.chromium.org/gerrit/#change,19560.)
Saw no failures.

Manual testing: Ran shill in VM, verified that ethernet
was identified as using virtio driver. Ran shill on an
Alex (with linksys usb ethernet adapter), verified that
ethernet was not identified as using virtio driver.

Change-Id: I48727e65c2f64f9973faaf9eeaedfbcf452b0b82
Reviewed-on: https://gerrit.chromium.org/gerrit/20415
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: mukesh agrawal <quiche@chromium.org>
Tested-by: mukesh agrawal <quiche@chromium.org>
diff --git a/virtio_ethernet.cc b/virtio_ethernet.cc
new file mode 100644
index 0000000..0e400e0
--- /dev/null
+++ b/virtio_ethernet.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/virtio_ethernet.h"
+
+#include <unistd.h>
+
+#include <string>
+
+#include <base/logging.h>
+
+#include "shill/control_interface.h"
+#include "shill/event_dispatcher.h"
+#include "shill/manager.h"
+
+using std::string;
+
+namespace shill {
+
+VirtioEthernet::VirtioEthernet(ControlInterface *control_interface,
+                               EventDispatcher *dispatcher,
+                               Metrics *metrics,
+                               Manager *manager,
+                               const string &link_name,
+                               const string &address,
+                               int interface_index)
+    : Ethernet(control_interface,
+               dispatcher,
+               metrics,
+               manager,
+               link_name,
+               address,
+               interface_index) {
+  VLOG(2) << "VirtioEthernet device " << link_name << " initialized.";
+}
+
+VirtioEthernet::~VirtioEthernet() {
+  // Nothing to be done beyond what Ethernet dtor does.
+}
+
+void VirtioEthernet::Start(Error *error,
+                           const EnabledStateChangedCallback &callback) {
+  // We are sometimes instantiated (by DeviceInfo) before the Linux kernel
+  // has completed the setup function for the device (virtio_net:virtnet_probe).
+  //
+  // Furthermore, setting the IFF_UP flag on the device (as done in
+  // Ethernet::Start) may cause the kernel IPv6 code to send packets even
+  // though virtnet_probe has not completed.
+  //
+  // When that happens, the device gets stuck in a state where it cannot
+  // transmit any frames. (See crosbug.com/29494)
+  //
+  // To avoid this, we sleep to let the device setup function complete.
+  VLOG(2) << "Sleeping to let virtio initialize.";
+  sleep(2);
+  VLOG(2) << "Starting virtio Ethernet.";
+  Ethernet::Start(error, callback);
+}
+
+}  // namespace shill