From 1400bbb7eb5b3dc4fc5997d319c14bfb12665151 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Fri, 19 Sep 2008 16:10:12 -0700 Subject: [PATCH] Use device notifier in Linux kernel switch for detecting port status changes. To report on link status and whether a port is administratively enabled or disabled, the reference switches poll. This change uses the Linux kernel's device notification system, so that polling isn't necessary. --- datapath/Modules.mk | 1 + datapath/datapath.c | 54 ++++++++++++------------------ datapath/datapath.h | 3 ++ datapath/dp_notify.c | 63 +++++++++++++++++++++++++++++++++++ datapath/linux-2.4/.gitignore | 1 + datapath/linux-2.6/.gitignore | 1 + 6 files changed, 90 insertions(+), 33 deletions(-) create mode 100644 datapath/dp_notify.c diff --git a/datapath/Modules.mk b/datapath/Modules.mk index 23e54da7..f21af16b 100644 --- a/datapath/Modules.mk +++ b/datapath/Modules.mk @@ -6,6 +6,7 @@ openflow_sources = \ crc32.c \ datapath.c \ dp_dev.c \ + dp_notify.c \ flow.c \ forward.c \ table-hash.c \ diff --git a/datapath/datapath.c b/datapath/datapath.c index 0ec226d9..564d3039 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -88,12 +88,10 @@ DEFINE_MUTEX(dp_mutex); EXPORT_SYMBOL(dp_mutex); static int dp_maint_func(void *data); -static int update_port_status(struct net_bridge_port *p); -static int send_port_status(struct net_bridge_port *p, uint8_t status); +static void init_port_status(struct net_bridge_port *p); static int dp_genl_openflow_done(struct netlink_callback *); static struct net_bridge_port *new_nbp(struct datapath *, struct net_device *, int port_no); -static int del_switch_port(struct net_bridge_port *); /* nla_shrink - reduce amount of space reserved by nla_reserve * @skb: socket buffer from which to recover room @@ -309,7 +307,7 @@ static int new_dp(int dp_idx) return 0; err_destroy_local_port: - del_switch_port(dp->local_port); + dp_del_switch_port(dp->local_port); err_destroy_chain: chain_destroy(dp->chain); err_destroy_dp_dev: @@ -378,16 +376,16 @@ int add_switch_port(struct datapath *dp, struct net_device *dev) if (IS_ERR(p)) return PTR_ERR(p); - update_port_status(p); + init_port_status(p); /* Notify the ctlpath that this port has been added */ - send_port_status(p, OFPPR_ADD); + dp_send_port_status(p, OFPPR_ADD); return 0; } /* Delete 'p' from switch. */ -static int del_switch_port(struct net_bridge_port *p) +int dp_del_switch_port(struct net_bridge_port *p) { /* First drop references to device. */ cancel_work_sync(&p->port_task); @@ -403,7 +401,7 @@ static int del_switch_port(struct net_bridge_port *p) synchronize_rcu(); /* Notify the ctlpath that this port no longer exists */ - send_port_status(p, OFPPR_DELETE); + dp_send_port_status(p, OFPPR_DELETE); dev_put(p->dev); kfree(p); @@ -419,7 +417,7 @@ static void del_dp(struct datapath *dp) /* Drop references to DP. */ list_for_each_entry_safe (p, n, &dp->port_list, node) - del_switch_port(p); + dp_del_switch_port(p); rcu_assign_pointer(dps[dp->dp_idx], NULL); /* Kill off local_port dev references from buffered packets that have @@ -443,15 +441,6 @@ static int dp_maint_func(void *data) struct datapath *dp = (struct datapath *) data; while (!kthread_should_stop()) { - struct net_bridge_port *p; - - /* Check if port status has changed */ - rcu_read_lock(); - list_for_each_entry_rcu (p, &dp->port_list, node) - if (update_port_status(p)) - send_port_status(p, OFPPR_MOD); - rcu_read_unlock(); - /* Timeout old entries */ chain_timeout(dp->chain); msleep_interruptible(MAINT_SLEEP_MSECS); @@ -886,20 +875,13 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm) return 0; } -/* 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 net_bridge_port *p) +/* Initialize the port status field of the bridge port. */ +static void +init_port_status(struct net_bridge_port *p) { unsigned long int flags; - uint32_t orig_status; spin_lock_irqsave(&p->lock, flags); - orig_status = p->status; if (p->dev->flags & IFF_UP) p->status &= ~OFPPFL_PORT_DOWN; @@ -912,11 +894,10 @@ update_port_status(struct net_bridge_port *p) p->status |= OFPPFL_LINK_DOWN; spin_unlock_irqrestore(&p->lock, flags); - return (orig_status != p->status); } -static int -send_port_status(struct net_bridge_port *p, uint8_t status) +int +dp_send_port_status(struct net_bridge_port *p, uint8_t status) { struct sk_buff *skb; struct ofp_port_status *ops; @@ -1162,7 +1143,7 @@ static int dp_genl_add_del_port(struct sk_buff *skb, struct genl_info *info) err = -ENOENT; goto out_put; } - err = del_switch_port(port->br_port); + err = dp_del_switch_port(port->br_port); } out_put: @@ -1740,10 +1721,14 @@ static int __init dp_init(void) if (err) goto error; - err = dp_init_netlink(); + err = register_netdevice_notifier(&dp_device_notifier); if (err) goto error_flow_exit; + err = dp_init_netlink(); + if (err) + goto error_unreg_notifier; + /* Hook into callback used by the bridge to intercept packets. * Parasites we are. */ if (br_handle_frame_hook) @@ -1752,6 +1737,8 @@ static int __init dp_init(void) return 0; +error_unreg_notifier: + unregister_netdevice_notifier(&dp_device_notifier); error_flow_exit: flow_exit(); error: @@ -1763,6 +1750,7 @@ static void dp_cleanup(void) { fwd_exit(); dp_uninit_netlink(); + unregister_netdevice_notifier(&dp_device_notifier); flow_exit(); br_handle_frame_hook = NULL; } diff --git a/datapath/datapath.h b/datapath/datapath.h index 01fbaae1..4339539a 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -84,7 +84,9 @@ struct net_bridge_port { }; extern struct mutex dp_mutex; +extern struct notifier_block dp_device_notifier; +int dp_del_switch_port(struct net_bridge_port *); int dp_output_port(struct datapath *, struct sk_buff *, int out_port, int ignore_no_fwd); int dp_output_control(struct datapath *, struct sk_buff *, uint32_t, @@ -92,6 +94,7 @@ int dp_output_control(struct datapath *, struct sk_buff *, uint32_t, int dp_set_origin(struct datapath *, uint16_t, struct sk_buff *); int dp_send_features_reply(struct datapath *, const struct sender *); int dp_send_config_reply(struct datapath *, const struct sender *); +int dp_send_port_status(struct net_bridge_port *p, uint8_t status); int dp_send_flow_expired(struct datapath *, struct sw_flow *, enum ofp_flow_expired_reason); int dp_send_error_msg(struct datapath *, const struct sender *, diff --git a/datapath/dp_notify.c b/datapath/dp_notify.c new file mode 100644 index 00000000..2885e7f0 --- /dev/null +++ b/datapath/dp_notify.c @@ -0,0 +1,63 @@ +/* + * Distributed under the terms of the GNU GPL version 2. + * Copyright (c) 2007, 2008 The Board of Trustees of The Leland + * Stanford Junior University + */ + +/* Handle changes to managed devices */ + +#include + +#include "datapath.h" + + +static int dp_device_event(struct notifier_block *unused, unsigned long event, + void *ptr) +{ + struct net_device *dev = ptr; + struct net_bridge_port *p = dev->br_port; + unsigned long int flags; + uint32_t orig_status; + + + /* Check if monitored port */ + if (!p) + return NOTIFY_DONE; + + spin_lock_irqsave(&p->lock, flags); + orig_status = p->status; + + switch (event) { + case NETDEV_CHANGE: + if (netif_carrier_ok(p->dev)) + p->status &= ~OFPPFL_LINK_DOWN; + else + p->status |= OFPPFL_LINK_DOWN; + break; + + case NETDEV_DOWN: + p->status |= OFPPFL_PORT_DOWN; + break; + + case NETDEV_UP: + p->status &= ~OFPPFL_PORT_DOWN; + break; + + case NETDEV_UNREGISTER: + /* xxx Make sure this is correct */ + spin_unlock_irqrestore(&p->lock, flags); + dp_del_switch_port(p); + return NOTIFY_DONE; + break; + } + spin_unlock_irqrestore(&p->lock, flags); + + if (orig_status != p->status) + dp_send_port_status(p, OFPPR_MOD); + + return NOTIFY_DONE; +} + +struct notifier_block dp_device_notifier = { + .notifier_call = dp_device_event +}; diff --git a/datapath/linux-2.4/.gitignore b/datapath/linux-2.4/.gitignore index 0b857d8d..739e1571 100644 --- a/datapath/linux-2.4/.gitignore +++ b/datapath/linux-2.4/.gitignore @@ -9,6 +9,7 @@ /crc32.c /crc_t.c /dp_dev.c +/dp_notify.c /flow.c /forward.c /forward_t.c diff --git a/datapath/linux-2.6/.gitignore b/datapath/linux-2.6/.gitignore index 634a8d99..56f38ada 100644 --- a/datapath/linux-2.6/.gitignore +++ b/datapath/linux-2.6/.gitignore @@ -6,6 +6,7 @@ /crc_t.c /datapath.c /dp_dev.c +/dp_notify.c /flow.c /forward.c /forward_t.c -- 2.30.2