[ovs-dev] [stats again 4/5] netdev: Pass link statistics as part of network device notifications.

Ben Pfaff blp at nicira.com
Fri Sep 17 17:41:46 UTC 2010


It is useful for network device statistics in the database to be accurate
at least when the devices are added to the database and just before they
are removed from the database.  Initial statistics are easy and they are
already implemented.  Final statistics are a little harder, since it's
difficult to obtain statistics from a device that no longer exists.
However, Linux actually provides final device statistics in the Netlink
message that it broadcasts to userspace to notify that the device has been
deleted.  Until now, the rtnetlink and netdev layers have not provided a
way to get at these statistics.  This commit adds support for passing them
up through all the various layers to their final consumer in ovs-vswitchd.
---
 lib/netdev-linux.c    |   71 +++++++++++++++++++++++++++++--------------------
 lib/netdev-provider.h |   42 ++++++++++++++++++++++------
 lib/netdev-vport.c    |    5 +--
 lib/netdev-vport.h    |    3 +-
 lib/netdev.c          |   35 +++++++++++++++++++-----
 lib/netdev.h          |    3 +-
 lib/rtnetlink.c       |    5 +++
 lib/rtnetlink.h       |    3 +-
 lib/shash.c           |   11 +++++++
 lib/shash.h           |    9 ++++++
 ofproto/ofproto.c     |   40 +++++++++++++++++----------
 ofproto/ofproto.h     |    3 +-
 vswitchd/bridge.c     |    1 +
 13 files changed, 163 insertions(+), 68 deletions(-)

diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index e6036bf..0c916a7 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -384,6 +384,8 @@ static int do_set_addr(struct netdev *netdev,
 static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]);
 static int set_etheraddr(const char *netdev_name, int hwaddr_family,
                          const uint8_t[ETH_ADDR_LEN]);
+static void rtnl_stats_to_netdev_stats(const struct rtnl_link_stats *,
+                                       struct netdev_stats *);
 static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
 static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
 
@@ -1979,12 +1981,12 @@ netdev_linux_update_flags(struct netdev *netdev, enum netdev_flags off,
 }
 
 static void
-poll_notify(struct list *list)
+poll_notify(struct list *list, const struct netdev_stats *stats)
 {
     struct netdev_linux_notifier *notifier;
     LIST_FOR_EACH (notifier, struct netdev_linux_notifier, node, list) {
         struct netdev_notifier *n = &notifier->notifier;
-        n->cb(n);
+        n->cb(n, stats);
     }
 }
 
@@ -1996,19 +1998,25 @@ netdev_linux_poll_cb(const struct rtnetlink_change *change,
         struct list *list = shash_find_data(&netdev_linux_notifiers,
                                             change->ifname);
         if (list) {
-            poll_notify(list);
+            if (change->stats) {
+                struct netdev_stats netdev_stats;
+
+                rtnl_stats_to_netdev_stats(change->stats, &netdev_stats);
+                poll_notify(list, &netdev_stats);
+            } else {
+                poll_notify(list, NULL);
+            }
         }
     } else {
         struct shash_node *node;
         SHASH_FOR_EACH (node, &netdev_linux_notifiers) {
-            poll_notify(node->data);
+            poll_notify(node->data, NULL);
         }
     }
 }
 
 static int
-netdev_linux_poll_add(struct netdev *netdev,
-                      void (*cb)(struct netdev_notifier *), void *aux,
+netdev_linux_poll_add(struct netdev *netdev, netdev_notifier_cb *cb, void *aux,
                       struct netdev_notifier **notifierp)
 {
     const char *netdev_name = netdev_get_name(netdev);
@@ -3257,6 +3265,33 @@ tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes)
 
 /* Utility functions. */
 
+static void
+rtnl_stats_to_netdev_stats(const struct rtnl_link_stats *rtnl_stats,
+                           struct netdev_stats *netdev_stats)
+{
+    netdev_stats->rx_packets = rtnl_stats->rx_packets;
+    netdev_stats->tx_packets = rtnl_stats->tx_packets;
+    netdev_stats->rx_bytes = rtnl_stats->rx_bytes;
+    netdev_stats->tx_bytes = rtnl_stats->tx_bytes;
+    netdev_stats->rx_errors = rtnl_stats->rx_errors;
+    netdev_stats->tx_errors = rtnl_stats->tx_errors;
+    netdev_stats->rx_dropped = rtnl_stats->rx_dropped;
+    netdev_stats->tx_dropped = rtnl_stats->tx_dropped;
+    netdev_stats->multicast = rtnl_stats->multicast;
+    netdev_stats->collisions = rtnl_stats->collisions;
+    netdev_stats->rx_length_errors = rtnl_stats->rx_length_errors;
+    netdev_stats->rx_over_errors = rtnl_stats->rx_over_errors;
+    netdev_stats->rx_crc_errors = rtnl_stats->rx_crc_errors;
+    netdev_stats->rx_frame_errors = rtnl_stats->rx_frame_errors;
+    netdev_stats->rx_fifo_errors = rtnl_stats->rx_fifo_errors;
+    netdev_stats->rx_missed_errors = rtnl_stats->rx_missed_errors;
+    netdev_stats->tx_aborted_errors = rtnl_stats->tx_aborted_errors;
+    netdev_stats->tx_carrier_errors = rtnl_stats->tx_carrier_errors;
+    netdev_stats->tx_fifo_errors = rtnl_stats->tx_fifo_errors;
+    netdev_stats->tx_heartbeat_errors = rtnl_stats->tx_heartbeat_errors;
+    netdev_stats->tx_window_errors = rtnl_stats->tx_window_errors;
+}
+
 static int
 get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
 {
@@ -3273,7 +3308,6 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
     struct ofpbuf request;
     struct ofpbuf *reply;
     struct ifinfomsg *ifi;
-    const struct rtnl_link_stats *rtnl_stats;
     struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
     int error;
 
@@ -3301,28 +3335,7 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
         return EPROTO;
     }
 
-    rtnl_stats = nl_attr_get(attrs[IFLA_STATS]);
-    stats->rx_packets = rtnl_stats->rx_packets;
-    stats->tx_packets = rtnl_stats->tx_packets;
-    stats->rx_bytes = rtnl_stats->rx_bytes;
-    stats->tx_bytes = rtnl_stats->tx_bytes;
-    stats->rx_errors = rtnl_stats->rx_errors;
-    stats->tx_errors = rtnl_stats->tx_errors;
-    stats->rx_dropped = rtnl_stats->rx_dropped;
-    stats->tx_dropped = rtnl_stats->tx_dropped;
-    stats->multicast = rtnl_stats->multicast;
-    stats->collisions = rtnl_stats->collisions;
-    stats->rx_length_errors = rtnl_stats->rx_length_errors;
-    stats->rx_over_errors = rtnl_stats->rx_over_errors;
-    stats->rx_crc_errors = rtnl_stats->rx_crc_errors;
-    stats->rx_frame_errors = rtnl_stats->rx_frame_errors;
-    stats->rx_fifo_errors = rtnl_stats->rx_fifo_errors;
-    stats->rx_missed_errors = rtnl_stats->rx_missed_errors;
-    stats->tx_aborted_errors = rtnl_stats->tx_aborted_errors;
-    stats->tx_carrier_errors = rtnl_stats->tx_carrier_errors;
-    stats->tx_fifo_errors = rtnl_stats->tx_fifo_errors;
-    stats->tx_heartbeat_errors = rtnl_stats->tx_heartbeat_errors;
-    stats->tx_window_errors = rtnl_stats->tx_window_errors;
+    rtnl_stats_to_netdev_stats(nl_attr_get(attrs[IFLA_STATS]), stats);
 
     ofpbuf_delete(reply);
 
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index c0ed4ef..2db4f48 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -85,19 +85,41 @@ static inline void netdev_assert_class(const struct netdev *netdev,
 {
     netdev_dev_assert_class(netdev_get_dev(netdev), netdev_class);
 }
+
+/* Network device notifiers. */
+
+/* Notifier callback function.  The core netdev code passes such a callback
+ * function to the "poll_add" member function of a netdev_class, with the
+ * expectation that the netdev implementation will call it when important
+ * properties of the netdev change (e.g. its Ethernet address) or when the
+ * netdev is destroyed.
+ *
+ * The 'notifier' is the netdev_notifier created by the "poll_add"
+ * implementation and returned to the core netdev code.  "stats" is optional
+ * and may be NULL, but if it is nonnull then it should reflect the network
+ * device's statistics at the time the event occurred.  (This is especially
+ * useful for reporting that a network device is being deleted, since the
+ * device's final statistics cannot be retrieved in the usual way in such a
+ * case.)  */
+struct netdev_notifier;
+typedef void netdev_notifier_cb(struct netdev_notifier *notifier,
+                                const struct netdev_stats *stats);
 
 /* A network device notifier.
  *
  * Network device implementations should use netdev_notifier_init() to
  * initialize this structure, but they may freely read its members after
- * initialization. */
+ * initialization.
+ *
+ * Most implementations of notifiers will probably want to "subclass" this by
+ * declaring and allocating a larger wrapper struct. */
 struct netdev_notifier {
     struct netdev *netdev;
-    void (*cb)(struct netdev_notifier *);
+    netdev_notifier_cb *cb;
     void *aux;
 };
 void netdev_notifier_init(struct netdev_notifier *, struct netdev *,
-                          void (*cb)(struct netdev_notifier *), void *aux);
+                          netdev_notifier_cb *, void *aux);
 
 /* Network device class structure, to be defined by each implementation of a
  * network device.
@@ -122,7 +144,10 @@ struct netdev_class {
     int (*init)(void);
 
     /* Performs periodic work needed by netdevs of this class.  May be null if
-     * no periodic work is necessary. */
+     * no periodic work is necessary.
+     *
+     * This is a good place from which to invoke callbacks registered with the
+     * "poll_add" member function. */
     void (*run)(void);
 
     /* Arranges for poll_block() to wake up if the "run" member function needs
@@ -535,11 +560,10 @@ struct netdev_class {
 
     /* Arranges for 'cb' to be called whenever one of the attributes of
      * 'netdev' changes and sets '*notifierp' to a newly created
-     * netdev_notifier that represents this arrangement.  The created notifier
-     * will have its 'netdev', 'cb', and 'aux' members set to the values of the
-     * corresponding parameters. */
-    int (*poll_add)(struct netdev *netdev,
-                    void (*cb)(struct netdev_notifier *notifier), void *aux,
+     * netdev_notifier that represents this arrangement.  The netdev
+     * implementation should initialize '*notifierp' with
+     * netdev_notifier_init(), passing 'netdev', 'cb', and 'aux'. */
+    int (*poll_add)(struct netdev *netdev, netdev_notifier_cb *cb, void *aux,
                     struct netdev_notifier **notifierp);
 
     /* Cancels poll notification for 'notifier'. */
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 0153ac7..6d02971 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -212,8 +212,7 @@ make_poll_name(const struct netdev *netdev)
 }
 
 int
-netdev_vport_poll_add(struct netdev *netdev,
-                      void (*cb)(struct netdev_notifier *), void *aux,
+netdev_vport_poll_add(struct netdev *netdev, netdev_notifier_cb *cb, void *aux,
                       struct netdev_notifier **notifierp)
 {
     char *poll_name = make_poll_name(netdev);
@@ -272,7 +271,7 @@ netdev_vport_poll_notify(const struct netdev *netdev)
         LIST_FOR_EACH (notifier, struct netdev_vport_notifier,
                        list_node, list) {
             struct netdev_notifier *n = &notifier->notifier;
-            n->cb(n);
+            n->cb(n, NULL);
         }
     }
 
diff --git a/lib/netdev-vport.h b/lib/netdev-vport.h
index b401660..72ee2af 100644
--- a/lib/netdev-vport.h
+++ b/lib/netdev-vport.h
@@ -34,8 +34,7 @@ int netdev_vport_update_flags(struct netdev *, enum netdev_flags off,
                               enum netdev_flags on,
                               enum netdev_flags *old_flagsp);
 
-int netdev_vport_poll_add(struct netdev *,
-                          void (*cb)(struct netdev_notifier *), void *aux,
+int netdev_vport_poll_add(struct netdev *, netdev_notifier_cb *, void *aux,
                           struct netdev_notifier **);
 void netdev_vport_poll_remove(struct netdev_notifier *);
 void netdev_vport_poll_notify(const struct netdev *);
diff --git a/lib/netdev.c b/lib/netdev.c
index 24c2a88..3390e38 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -1438,7 +1438,7 @@ netdev_get_dev(const struct netdev *netdev)
  * notification will consist of calling 'cb', with auxiliary data 'aux'. */
 void
 netdev_notifier_init(struct netdev_notifier *notifier, struct netdev *netdev,
-                     void (*cb)(struct netdev_notifier *), void *aux)
+                     netdev_notifier_cb *cb, void *aux)
 {
     notifier->netdev = netdev;
     notifier->cb = cb;
@@ -1447,8 +1447,9 @@ netdev_notifier_init(struct netdev_notifier *notifier, struct netdev *netdev,
 
 /* Tracks changes in the status of a set of network devices. */
 struct netdev_monitor {
-    struct shash polled_netdevs;
-    struct shash changed_netdevs;
+    struct shash polled_netdevs; /* Contains "struct netdev_notifier *"s. */
+    struct shash changed_netdevs; /* Contains "struct netdev_stats *"s,
+                                     when non-null. */
 };
 
 /* Creates and returns a new structure for monitor changes in the status of
@@ -1476,17 +1477,29 @@ netdev_monitor_destroy(struct netdev_monitor *monitor)
         }
 
         shash_destroy(&monitor->polled_netdevs);
-        shash_destroy(&monitor->changed_netdevs);
+        shash_destroy_free_data(&monitor->changed_netdevs);
         free(monitor);
     }
 }
 
 static void
-netdev_monitor_cb(struct netdev_notifier *notifier)
+netdev_monitor_cb(struct netdev_notifier *notifier,
+                  const struct netdev_stats *stats)
 {
     struct netdev_monitor *monitor = notifier->aux;
     const char *name = netdev_get_name(notifier->netdev);
-    shash_add_once(&monitor->changed_netdevs, name, NULL);
+    struct shash_node *node;
+
+    node = shash_find_or_add(&monitor->changed_netdevs, name);
+    if (stats) {
+        struct netdev_stats *node_stats;
+
+        if (!node->data) {
+            node->data = xmalloc(sizeof *stats);
+        }
+        node_stats = node->data;
+        *node_stats = *stats;
+    }
 }
 
 /* Attempts to add 'netdev' as a netdev monitored by 'monitor'.  Returns 0 if
@@ -1532,6 +1545,7 @@ netdev_monitor_remove(struct netdev_monitor *monitor, struct netdev *netdev)
         /* Drop any pending notification. */
         node = shash_find(&monitor->changed_netdevs, netdev_name);
         if (node) {
+            free(node->data);
             shash_delete(&monitor->changed_netdevs, node);
         }
     }
@@ -1546,14 +1560,21 @@ netdev_monitor_remove(struct netdev_monitor *monitor, struct netdev *netdev)
  * If no devices have changed, sets '*devnamep' to NULL and returns EAGAIN.
  */
 int
-netdev_monitor_poll(struct netdev_monitor *monitor, char **devnamep)
+netdev_monitor_poll(struct netdev_monitor *monitor, char **devnamep,
+                    struct netdev_stats **statsp)
 {
     struct shash_node *node = shash_first(&monitor->changed_netdevs);
     if (!node) {
         *devnamep = NULL;
         return EAGAIN;
     } else {
+        struct netdev_stats *stats = node->data;
         *devnamep = xstrdup(node->name);
+        if (statsp) {
+            *statsp = stats;
+        } else {
+            free(stats);
+        }
         shash_delete(&monitor->changed_netdevs, node);
         return 0;
     }
diff --git a/lib/netdev.h b/lib/netdev.h
index cd5c8c3..2880928 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -209,7 +209,8 @@ struct netdev_monitor *netdev_monitor_create(void);
 void netdev_monitor_destroy(struct netdev_monitor *);
 int netdev_monitor_add(struct netdev_monitor *, struct netdev *);
 void netdev_monitor_remove(struct netdev_monitor *, struct netdev *);
-int netdev_monitor_poll(struct netdev_monitor *, char **devnamep);
+int netdev_monitor_poll(struct netdev_monitor *,
+                        char **devnamep, struct netdev_stats **);
 void netdev_monitor_poll_wait(const struct netdev_monitor *);
 
 #ifdef  __cplusplus
diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c
index f5a6df8..f55fe2f 100644
--- a/lib/rtnetlink.c
+++ b/lib/rtnetlink.c
@@ -106,6 +106,8 @@ rtnetlink_notifier_run(void)
         static const struct nl_policy rtnetlink_policy[] = {
             [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
             [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
+            [IFLA_STATS] = { .type = NL_A_UNSPEC, .optional = true,
+                             .min_len = sizeof(struct rtnl_link_stats) },
         };
 
         struct nlattr *attrs[ARRAY_SIZE(rtnetlink_policy)];
@@ -165,6 +167,9 @@ rtnetlink_report_change(const struct nlmsghdr *nlmsg,
     change.ifname = nl_attr_get_string(attrs[IFLA_IFNAME]);
     change.master_ifindex = (attrs[IFLA_MASTER]
                              ? nl_attr_get_u32(attrs[IFLA_MASTER]) : 0);
+    change.stats = (attrs[IFLA_STATS]
+                    ? nl_attr_get(attrs[IFLA_STATS])
+                    : NULL);
 
     LIST_FOR_EACH (notifier, struct rtnetlink_notifier, node,
                    &all_notifiers) {
diff --git a/lib/rtnetlink.h b/lib/rtnetlink.h
index 8f18805..b1933ac 100644
--- a/lib/rtnetlink.h
+++ b/lib/rtnetlink.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ struct rtnetlink_change {
     /* Extracted from Netlink attributes. */
     const char *ifname;         /* Name of network device. */
     int master_ifindex;         /* Ifindex of datapath master (0 if none). */
+    const struct rtnl_link_stats *stats; /* IFLA_STATS (null if missing) */
 };
 
 /* Function called to report that a netdev has changed.  'change' describes the
diff --git a/lib/shash.c b/lib/shash.c
index 8fd2eb1..dd7068f 100644
--- a/lib/shash.c
+++ b/lib/shash.c
@@ -224,6 +224,17 @@ shash_find_and_delete_assert(struct shash *sh, const char *name)
 }
 
 struct shash_node *
+shash_find_or_add(struct shash *sh, const char *name)
+{
+    size_t hash = hash_name(name);
+    struct shash_node *node = shash_find__(sh, name, hash);
+    if (!node) {
+        node = shash_add_nocopy__(sh, xstrdup(name), NULL, hash);
+    }
+    return node;
+}
+
+struct shash_node *
 shash_first(const struct shash *shash)
 {
     struct hmap_node *node = hmap_first(&shash->map);
diff --git a/lib/shash.h b/lib/shash.h
index eab0af4..f887887 100644
--- a/lib/shash.h
+++ b/lib/shash.h
@@ -46,22 +46,31 @@ void shash_init(struct shash *);
 void shash_destroy(struct shash *);
 void shash_destroy_free_data(struct shash *);
 void shash_swap(struct shash *, struct shash *);
+
 void shash_moved(struct shash *);
+
 void shash_clear(struct shash *);
 void shash_clear_free_data(struct shash *);
+
 bool shash_is_empty(const struct shash *);
 size_t shash_count(const struct shash *);
+
 struct shash_node *shash_add(struct shash *, const char *, const void *);
 struct shash_node *shash_add_nocopy(struct shash *, char *, const void *);
 bool shash_add_once(struct shash *, const char *, const void *);
 void shash_add_assert(struct shash *, const char *, const void *);
 void *shash_replace(struct shash *, const char *, const void *data);
+
 void shash_delete(struct shash *, struct shash_node *);
+
 struct shash_node *shash_find(const struct shash *, const char *);
 void *shash_find_data(const struct shash *, const char *);
 void *shash_find_and_delete(struct shash *, const char *);
 void *shash_find_and_delete_assert(struct shash *, const char *);
+struct shash_node *shash_find_or_add(struct shash *, const char *);
+
 struct shash_node *shash_first(const struct shash *);
+
 const struct shash_node **shash_sort(const struct shash *);
 bool shash_equal_keys(const struct shash *, const struct shash *);
 struct shash_node *shash_random_node(struct shash *);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 844083d..422a5d0 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -336,7 +336,8 @@ static void handle_openflow(struct ofconn *, struct ofproto *,
 
 static void refresh_port_groups(struct ofproto *);
 
-static void update_port(struct ofproto *, const char *devname);
+static void update_port(struct ofproto *, const char *devname,
+                        const struct netdev_stats *stats);
 static int init_ports(struct ofproto *);
 static void reinit_ports(struct ofproto *);
 
@@ -1003,12 +1004,13 @@ ofproto_run(struct ofproto *p)
 }
 
 static void
-process_port_change(struct ofproto *ofproto, int error, char *devname)
+process_port_change(struct ofproto *ofproto, int error, char *devname,
+                    const struct netdev_stats *stats)
 {
     if (error == ENOBUFS) {
         reinit_ports(ofproto);
     } else if (!error) {
-        update_port(ofproto, devname);
+        update_port(ofproto, devname, stats);
         free(devname);
     }
 }
@@ -1061,6 +1063,7 @@ ofproto_run1(struct ofproto *p)
 {
     struct ofconn *ofconn, *next_ofconn;
     struct ofservice *ofservice;
+    struct netdev_stats *stats;
     char *devname;
     int error;
     int i;
@@ -1091,11 +1094,12 @@ ofproto_run1(struct ofproto *p)
     }
 
     while ((error = dpif_port_poll(p->dpif, &devname)) != EAGAIN) {
-        process_port_change(p, error, devname);
+        process_port_change(p, error, devname, NULL);
     }
     while ((error = netdev_monitor_poll(p->netdev_monitor,
-                                        &devname)) != EAGAIN) {
-        process_port_change(p, error, devname);
+                                        &devname, &stats)) != EAGAIN) {
+        process_port_change(p, error, devname, stats);
+        free(stats);
     }
 
     if (p->in_band) {
@@ -1362,7 +1366,7 @@ reinit_ports(struct ofproto *p)
 
     svec_sort_unique(&devnames);
     for (i = 0; i < devnames.n; i++) {
-        update_port(p, devnames.names[i]);
+        update_port(p, devnames.names[i], NULL);
     }
     svec_destroy(&devnames);
 }
@@ -1497,9 +1501,6 @@ send_port_status(struct ofproto *p, const struct ofport *ofport,
         hton_ofp_phy_port(&ops->desc);
         queue_tx(b, ofconn, NULL);
     }
-    if (p->ofhooks->port_changed_cb) {
-        p->ofhooks->port_changed_cb(reason, &ofport->opp, p->aux);
-    }
 }
 
 static void
@@ -1540,11 +1541,14 @@ ofport_free(struct ofport *ofport)
 }
 
 static void
-update_port(struct ofproto *p, const char *devname)
+update_port(struct ofproto *p, const char *devname,
+            const struct netdev_stats *stats)
 {
     struct odp_port odp_port;
     struct ofport *old_ofport;
     struct ofport *new_ofport;
+    struct ofport *ofport;
+    uint8_t reason;
     int error;
 
     COVERAGE_INC(ofproto_update_port);
@@ -1601,10 +1605,16 @@ update_port(struct ofproto *p, const char *devname)
     if (new_ofport) {
         ofport_install(p, new_ofport);
     }
-    send_port_status(p, new_ofport ? new_ofport : old_ofport,
-                     (!old_ofport ? OFPPR_ADD
-                      : !new_ofport ? OFPPR_DELETE
-                      : OFPPR_MODIFY));
+
+    /* Send notifications to OpenFlow controllers and hooks. */
+    reason = (!old_ofport ? OFPPR_ADD
+              : !new_ofport ? OFPPR_DELETE
+              : OFPPR_MODIFY);
+    ofport = new_ofport ? new_ofport : old_ofport;
+    send_port_status(p, ofport, reason);
+    if (p->ofhooks->port_changed_cb) {
+        p->ofhooks->port_changed_cb(reason, &ofport->opp, stats, p->aux);
+    }
     ofport_free(old_ofport);
 
     /* Update port groups. */
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 2248451..803be0c 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -30,6 +30,7 @@
 extern "C" {
 #endif
 
+struct netdev_stats;
 struct odp_actions;
 struct ofhooks;
 struct ofproto;
@@ -139,7 +140,7 @@ void ofproto_flush_flows(struct ofproto *);
 /* Hooks for ovs-vswitchd. */
 struct ofhooks {
     void (*port_changed_cb)(enum ofp_port_reason, const struct ofp_phy_port *,
-                            void *aux);
+                            const struct netdev_stats *, void *aux);
     bool (*normal_cb)(const flow_t *, const struct ofpbuf *packet,
                       struct odp_actions *, tag_type *,
                       uint16_t *nf_output_iface, void *aux);
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 7b4f502..ee4d7f8 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -2473,6 +2473,7 @@ done:
 static void
 bridge_port_changed_ofhook_cb(enum ofp_port_reason reason,
                               const struct ofp_phy_port *opp,
+                              const struct netdev_stats *stats,
                               void *br_)
 {
     struct bridge *br = br_;
-- 
1.7.1





More information about the dev mailing list