PATCH idea
Subsystems (3)
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?
Signature · 4 lines
Tim Hockin Systems Software Engineer Sun Microsystems, Linux Kernel Engineering [email protected]
===== drivers/net/net_init.c 1.9 vs edited =====
drivers/net/net_init.c +2 -0
@@ -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 =====
include/linux/netdevice.h +5 -0
@@ -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 =====
include/linux/workqueue.h +9 -1
@@ -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 =====
kernel/workqueue.c +27 -1
@@ -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 =====
net/netsyms.c +1 -0
@@ -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 =====
net/core/dev.c +14 -0
@@ -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