struct net_device *dev = ptr;
struct net_bridge_port *p = dev->br_port;
unsigned long int flags;
- uint32_t orig_state, orig_config;
/* Check if monitored port */
return NOTIFY_DONE;
spin_lock_irqsave(&p->lock, flags);
- orig_state = p->state;
- orig_config = p->config;
-
switch (event) {
- case NETDEV_CHANGE:
- if (netif_carrier_ok(p->dev))
- p->state &= ~OFPPS_LINK_DOWN;
- else
- p->state |= OFPPS_LINK_DOWN;
- break;
-
- case NETDEV_DOWN:
- p->config |= OFPPC_PORT_DOWN;
- break;
-
- case NETDEV_UP:
- p->config &= ~OFPPC_PORT_DOWN;
- break;
-
case NETDEV_UNREGISTER:
spin_unlock_irqrestore(&p->lock, flags);
mutex_lock(&dp_mutex);
}
spin_unlock_irqrestore(&p->lock, flags);
- if ((orig_state != p->state) || (orig_config != p->config))
- dp_send_port_status(p, OFPPR_MODIFY);
-
return NOTIFY_DONE;
}
#include "poll-loop.h"
#include "port-array.h"
#include "rconn.h"
+#include "shash.h"
+#include "svec.h"
#include "timeval.h"
#include "vconn.h"
#include "xtoxll.h"
struct port_watcher_local_cb local_cbs[4];
int n_local_cbs;
char local_port_name[OFP_MAX_PORT_NAME_LEN + 1];
+ struct netdev_monitor *mon;
+ struct shash port_by_name;
};
/* Returns the number of fields that differ from 'a' to 'b'. */
}
}
+static void
+update_netdev_monitor_devices(struct port_watcher *pw)
+{
+ struct ofp_phy_port *p;
+ struct svec netdevs;
+ unsigned int port_no;
+
+ svec_init(&netdevs);
+ shash_clear(&pw->port_by_name);
+ for (p = port_array_first(&pw->ports, &port_no); p;
+ p = port_array_next(&pw->ports, &port_no)) {
+ const char *name = (const char *) p->name;
+ svec_add(&netdevs, name);
+ shash_add(&pw->port_by_name, name, p);
+ }
+ netdev_monitor_set_devices(pw->mon, netdevs.names, netdevs.n);
+ svec_destroy(&netdevs);
+}
+
static bool
port_watcher_local_packet_cb(struct relay *r, void *pw_)
{
}
}
+ update_netdev_monitor_devices(pw);
+
call_local_port_changed_callbacks(pw);
} else if (oh->type == OFPT_PORT_STATUS
&& msg->size >= sizeof(struct ofp_port_status)) {
if (ops->desc.port_no == htons(OFPP_LOCAL)) {
call_local_port_changed_callbacks(pw);
}
+ if (ops->reason == OFPPR_ADD || OFPPR_DELETE) {
+ update_netdev_monitor_devices(pw);
+ }
}
return false;
}
return false;
}
+/* Sets 'bit' in '*word' to 0 or 1 according to 'value'. */
+static void
+set_bit(uint32_t bit, bool value, uint32_t *word)
+{
+ if (value) {
+ *word |= bit;
+ } else {
+ *word &= ~bit;
+ }
+}
+
static void
port_watcher_periodic_cb(void *pw_)
{
struct port_watcher *pw = pw_;
+ const char *name;
if (!pw->got_feature_reply
&& time_now() >= pw->last_feature_request + 5
rconn_send_with_limit(pw->local_rconn, b, &pw->n_txq, 1);
pw->last_feature_request = time_now();
}
+
+ netdev_monitor_run(pw->mon);
+ while ((name = netdev_monitor_poll(pw->mon)) != NULL) {
+ struct ofp_phy_port *opp;
+ struct ofp_phy_port new_opp;
+ enum netdev_flags flags;
+ int retval;
+
+ opp = shash_find_data(&pw->port_by_name, name);
+ if (!opp) {
+ continue;
+ }
+
+ retval = netdev_nodev_get_flags(name, &flags);
+ if (retval) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "could not get flags for %s", name);
+ continue;
+ }
+
+ new_opp = *opp;
+ set_bit(htonl(OFPPC_PORT_DOWN), flags & NETDEV_UP, &new_opp.config);
+ set_bit(htonl(OFPPS_LINK_DOWN), flags & NETDEV_CARRIER,
+ &new_opp.state);
+ if (opp->config != new_opp.config || opp->state != new_opp.state) {
+ struct ofp_port_status *ops;
+ struct ofpbuf *b;
+
+ /* Notify other secchan modules. */
+ update_phy_port(pw, &new_opp, OFPPR_MODIFY);
+ if (new_opp.port_no == htons(OFPP_LOCAL)) {
+ call_local_port_changed_callbacks(pw);
+ }
+
+ /* Notify the controller that the flags changed. */
+ ops = make_openflow(sizeof *ops, OFPT_PORT_STATUS, &b);
+ ops->reason = OFPPR_MODIFY;
+ ops->desc = new_opp;
+ rconn_send(pw->remote_rconn, b, NULL);
+ }
+ }
}
static void
poll_immediate_wake();
}
}
+ netdev_monitor_wait(pw->mon);
}
static void
struct port_watcher **pwp)
{
struct port_watcher *pw;
+ int retval;
pw = *pwp = xcalloc(1, sizeof *pw);
pw->local_rconn = local_rconn;
pw->last_feature_request = TIME_MIN;
port_array_init(&pw->ports);
pw->local_port_name[0] = '\0';
+ retval = netdev_monitor_create(&pw->mon);
+ if (retval) {
+ ofp_fatal(retval, "failed to start network device monitoring");
+ }
+ shash_init(&pw->port_by_name);
port_watcher_register_callback(pw, log_port_status, NULL);
add_hook(secchan, &port_watcher_hook_class, pw);
}
static void remote_destroy(struct remote *);
static void update_port_flags(struct datapath *, const struct ofp_port_mod *);
-static int update_port_status(struct sw_port *p);
static void send_port_status(struct sw_port *p, uint8_t status);
/* Buffers are identified by a 31-bit opaque ID. We divide the ID
struct list deleted = LIST_INITIALIZER(&deleted);
struct sw_flow *f, *n;
- LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
- if (update_port_status(p)) {
- send_port_status(p, OFPPR_MODIFY);
- }
- }
-
chain_timeout(dp->chain, &deleted);
LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) {
dp_send_flow_end(dp, f, f->reason);
}
}
-/* Update the port status field of the bridge port. A non-zero return
- * value indicates some field has changed.
- *
- * NB: Callers of this function may hold the RCU read lock, so any
- * additional checks must not sleep.
- */
-static int
-update_port_status(struct sw_port *p)
-{
- int retval;
- enum netdev_flags flags;
- uint32_t orig_config = p->config;
- uint32_t orig_state = p->state;
-
- if (netdev_get_flags(p->netdev, &flags) < 0) {
- VLOG_WARN_RL(&rl, "could not get netdev flags for %s",
- netdev_get_name(p->netdev));
- return 0;
- }
-
- if (flags & NETDEV_UP) {
- p->config &= ~OFPPC_PORT_DOWN;
- } else {
- p->config |= OFPPC_PORT_DOWN;
- }
-
- if (flags & NETDEV_CARRIER) {
- p->state &= ~OFPPS_LINK_DOWN;
- } else {
- p->state |= OFPPS_LINK_DOWN;
- }
-
- return ((orig_config != p->config) || (orig_state != p->state));
-}
-
static void
send_port_status(struct sw_port *p, uint8_t status)
{