Looking for feedback on this quickie patch. This enables netlink to
deliver link-change events, as reported by drivers via
netif_carrier_{on,off}.
* We could use a different RTM than NEWLINK (RTM_LINKCHANGE?) if we care
* We could do notifier_call_chain(netdev_chain) with NETDEV_UP or
NETDEV_LINKCHANGE (new) if we care
comments?
-- Tim Hockin Systems Software Engineer Sun Microsystems, Linux Kernel Engineering thockin@sun.com
===== drivers/net/net_init.c 1.9 vs edited ===== --- 1.9/drivers/net/net_init.c Mon Feb 4 23:44:55 2002 +++ edited/drivers/net/net_init.c Thu Oct 10 11:44:18 2002 @@ -93,6 +93,7 @@ dev->priv = (void *) (((long)(dev + 1) + 31) & ~31); setup(dev); + INIT_WORK(&dev->link_work, NULL, NULL); strcpy(dev->name, mask); return dev; @@ -163,6 +164,7 @@ */ setup(dev); + INIT_WORK(&dev->link_work, NULL, NULL); if (new_device) { int err; ===== include/linux/netdevice.h 1.24 vs edited ===== --- 1.24/include/linux/netdevice.h Fri Oct 4 19:17:35 2002 +++ edited/include/linux/netdevice.h Thu Oct 10 14:56:23 2002 @@ -35,6 +35,7 @@ #ifdef __KERNEL__ #include <linux/config.h> +#include <linux/workqueue.h> #ifdef CONFIG_NET_PROFILE #include <net/profile.h> #endif @@ -437,6 +438,7 @@ /* this will get initialized at each interface type init routine */ struct divert_blk *divert; #endif /* CONFIG_NET_DIVERT */ + struct work_struct link_work; }; @@ -637,17 +639,20 @@ } extern void __netdev_watchdog_up(struct net_device *dev); +extern void netdev_do_link_work(struct net_device *dev); static inline void netif_carrier_on(struct net_device *dev) { clear_bit(__LINK_STATE_NOCARRIER, &dev->state); if (netif_running(dev)) __netdev_watchdog_up(dev); + netdev_do_link_work(dev); } static inline void netif_carrier_off(struct net_device *dev) { set_bit(__LINK_STATE_NOCARRIER, &dev->state); + netdev_do_link_work(dev); } /* Hot-plugging. */ ===== include/linux/workqueue.h 1.3 vs edited ===== --- 1.3/include/linux/workqueue.h Thu Oct 3 14:29:58 2002 +++ edited/include/linux/workqueue.h Thu Oct 10 16:50:36 2002 @@ -7,6 +7,7 @@ #include <linux/timer.h> #include <linux/linkage.h> +#include <linux/spinlock.h> struct workqueue_struct; @@ -17,12 +18,15 @@ void *data; void *wq_data; struct timer_list timer; + spinlock_t lock; }; #define __WORK_INITIALIZER(n, f, d) { \ .entry = { &(n).entry, &(n).entry }, \ + .pending = 0, \ .func = (f), \ - .data = (d) } + .data = (d), \ + .lock = SPIN_LOCK_UNLOCKED } #define DECLARE_WORK(n, f, d) \ struct work_struct n = __WORK_INITIALIZER(n, f, d) @@ -45,6 +49,7 @@ (_work)->pending = 0; \ PREPARE_WORK((_work), (_func), (_data)); \ init_timer(&(_work)->timer); \ + spin_lock_init(&(_work)->lock); \ } while (0) extern struct workqueue_struct *create_workqueue(const char *name); @@ -56,10 +61,13 @@ extern int FASTCALL(schedule_work(struct work_struct *work)); extern int FASTCALL(schedule_delayed_work(struct work_struct *work, unsigned long delay)); +extern int FASTCALL(cancel_work(struct work_struct *work)); extern void flush_scheduled_work(void); extern int current_is_keventd(void); extern void init_workqueues(void); + +#define work_pending(_work) test_bit(0, &(_work)->pending) #endif ===== kernel/workqueue.c 1.3 vs edited ===== --- 1.3/kernel/workqueue.c Thu Oct 3 05:37:56 2002 +++ edited/kernel/workqueue.c Thu Oct 10 17:12:28 2002 @@ -140,10 +140,13 @@ void *data = work->data; list_del_init(cwq->worklist.next); - spin_unlock_irqrestore(&cwq->lock, flags); + spin_lock(&work->lock); + spin_unlock(&cwq->lock); BUG_ON(work->wq_data != cwq); clear_bit(0, &work->pending); + spin_unlock_irqrestore(&work->lock, flags); + f(data); /* @@ -354,6 +357,28 @@ flush_workqueue(keventd_wq); } +int cancel_work(struct work_struct *work) +{ + unsigned long flags; + struct cpu_workqueue_struct *cwq; + int cancelled = 0; + + /* this should be safe to read - it only races with queue_*work() */ + cwq = work->wq_data; + + spin_lock_irqsave(&cwq->lock, flags); + spin_lock(&work->lock); + if (work_pending(work)) { + list_del_init(&work->entry); + clear_bit(0, &work->pending); + cancelled = 1; + } + spin_unlock(&work->lock); + spin_unlock_irqrestore(&cwq->lock, flags); + + return cancelled; +} + int current_is_keventd(void) { struct cpu_workqueue_struct *cwq; @@ -386,4 +411,5 @@ EXPORT_SYMBOL(schedule_work); EXPORT_SYMBOL(schedule_delayed_work); EXPORT_SYMBOL(flush_scheduled_work); +EXPORT_SYMBOL(cancel_work); ===== net/netsyms.c 1.29 vs edited ===== --- 1.29/net/netsyms.c Fri Oct 4 19:17:35 2002 +++ edited/net/netsyms.c Thu Oct 10 14:57:53 2002 @@ -471,6 +471,7 @@ EXPORT_SYMBOL(register_netdevice); EXPORT_SYMBOL(unregister_netdevice); EXPORT_SYMBOL(netdev_state_change); +EXPORT_SYMBOL(netdev_do_link_work); EXPORT_SYMBOL(dev_new_index); EXPORT_SYMBOL(dev_get_by_index); EXPORT_SYMBOL(__dev_get_by_index); ===== net/core/dev.c 1.41 vs edited ===== --- 1.41/net/core/dev.c Mon Oct 7 05:31:06 2002 +++ edited/net/core/dev.c Thu Oct 10 16:05:06 2002 @@ -629,6 +629,20 @@ } } +static void do_link_work(void *cookie) +{ + struct net_device *dev = cookie; + rtnl_lock(); + rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_RUNNING); + rtnl_unlock(); +} + +void netdev_do_link_work(struct net_device *dev) +{ + cancel_work(&dev->link_work); + PREPARE_WORK(&dev->link_work, do_link_work, dev); + schedule_work(&dev->link_work); +} #ifdef CONFIG_KMOD
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
This archive was generated by hypermail 2b29 : Tue Oct 15 2002 - 22:00:40 EST