+static void
+netdev_dummy_run(void)
+{
+ struct shash dummy_netdevs;
+ struct shash_node *node;
+
+ shash_init(&dummy_netdevs);
+ netdev_get_devices(&dummy_class, &dummy_netdevs);
+ SHASH_FOR_EACH (node, &dummy_netdevs) {
+ struct netdev_dummy *dev = node->data;
+ size_t i;
+
+ if (dev->pstream) {
+ struct stream *new_stream;
+ int error;
+
+ error = pstream_accept(dev->pstream, &new_stream);
+ if (!error) {
+ struct dummy_stream *s;
+
+ dev->streams = xrealloc(dev->streams,
+ ((dev->n_streams + 1)
+ * sizeof *dev->streams));
+ s = &dev->streams[dev->n_streams++];
+ s->stream = new_stream;
+ ofpbuf_init(&s->rxbuf, 2048);
+ list_init(&s->txq);
+ } else if (error != EAGAIN) {
+ VLOG_WARN("%s: accept failed (%s)",
+ pstream_get_name(dev->pstream), ovs_strerror(error));
+ pstream_close(dev->pstream);
+ dev->pstream = NULL;
+ }
+ }
+
+ for (i = 0; i < dev->n_streams; i++) {
+ struct dummy_stream *s = &dev->streams[i];
+ int error = 0;
+ size_t n;
+
+ stream_run(s->stream);
+
+ if (!list_is_empty(&s->txq)) {
+ struct ofpbuf *txbuf;
+ int retval;
+
+ txbuf = ofpbuf_from_list(list_front(&s->txq));
+ retval = stream_send(s->stream, txbuf->data, txbuf->size);
+ if (retval > 0) {
+ ofpbuf_pull(txbuf, retval);
+ if (!txbuf->size) {
+ list_remove(&txbuf->list_node);
+ ofpbuf_delete(txbuf);
+ }
+ } else if (retval != -EAGAIN) {
+ error = -retval;
+ }
+ }
+
+ if (!error) {
+ if (s->rxbuf.size < 2) {
+ n = 2 - s->rxbuf.size;
+ } else {
+ uint16_t frame_len;
+
+ frame_len = ntohs(get_unaligned_be16(s->rxbuf.data));
+ if (frame_len < ETH_HEADER_LEN) {
+ error = EPROTO;
+ n = 0;
+ } else {
+ n = (2 + frame_len) - s->rxbuf.size;
+ }
+ }
+ }
+ if (!error) {
+ int retval;
+
+ ofpbuf_prealloc_tailroom(&s->rxbuf, n);
+ retval = stream_recv(s->stream, ofpbuf_tail(&s->rxbuf), n);
+ if (retval > 0) {
+ s->rxbuf.size += retval;
+ if (retval == n && s->rxbuf.size > 2) {
+ ofpbuf_pull(&s->rxbuf, 2);
+ netdev_dummy_queue_packet(dev,
+ ofpbuf_clone(&s->rxbuf));
+ ofpbuf_clear(&s->rxbuf);
+ }
+ } else if (retval != -EAGAIN) {
+ error = (retval < 0 ? -retval
+ : s->rxbuf.size ? EPROTO
+ : EOF);
+ }
+ }
+
+ if (error) {
+ VLOG_DBG("%s: closing connection (%s)",
+ stream_get_name(s->stream),
+ ovs_retval_to_string(error));
+ dummy_stream_close(&dev->streams[i]);
+ dev->streams[i] = dev->streams[--dev->n_streams];
+ }
+ }
+
+ netdev_close(&dev->up);
+ }
+ shash_destroy(&dummy_netdevs);
+}
+
+static void
+dummy_stream_close(struct dummy_stream *s)
+{
+ stream_close(s->stream);
+ ofpbuf_uninit(&s->rxbuf);
+ ofpbuf_list_delete(&s->txq);
+}
+
+static void
+netdev_dummy_wait(void)