In order to support invoking the notifiers in the reverse order, we need to be able to traverse the callback chain in both directions. So convert the notifier list into a circular doubly linked list.
Signed-off-by: Srivatsa S. Bhat <srivatsa.b...@linux.vnet.ibm.com> --- arch/mips/powertv/powertv_setup.c | 2 arch/um/drivers/mconsole_kern.c | 2 arch/um/kernel/um_arch.c | 2 drivers/acpi/sleep.c | 2 drivers/char/ipmi/ipmi_msghandler.c | 2 drivers/char/ipmi/ipmi_watchdog.c | 4 - drivers/firmware/dcdbas.c | 2 drivers/md/md.c | 2 drivers/net/ethernet/intel/igb/igb_main.c | 2 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 2 drivers/staging/vt6655/device_main.c | 2 include/linux/notifier.h | 23 +++-- kernel/debug/debug_core.c | 2 kernel/notifier.c | 99 +++++++++++----------- kernel/trace/trace.c | 2 mm/page-writeback.c | 2 mm/vmstat.c | 4 - 18 files changed, 81 insertions(+), 77 deletions(-) diff --git a/arch/mips/powertv/powertv_setup.c b/arch/mips/powertv/powertv_setup.c index 3933c37..bade798 100644 --- a/arch/mips/powertv/powertv_setup.c +++ b/arch/mips/powertv/powertv_setup.c @@ -87,7 +87,7 @@ static void register_panic_notifier() { static struct notifier_block panic_notifier = { .notifier_call = panic_handler, - .next = NULL, + .list = LIST_HEAD_INIT(panic_notifier.list), .priority = INT_MAX }; atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier); diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 88e466b..dea89210 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -898,7 +898,7 @@ static int notify_panic(struct notifier_block *self, unsigned long unused1, static struct notifier_block panic_exit_notifier = { .notifier_call = notify_panic, - .next = NULL, + .list = LIST_HEAD_INIT(panic_exit_notifier.list), .priority = 1 }; diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 4db8770..fdd2b78 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -243,7 +243,7 @@ static int panic_exit(struct notifier_block *self, unsigned long unused1, static struct notifier_block panic_exit_notifier = { .notifier_call = panic_exit, - .next = NULL, + .list = LIST_HEAD_INIT(panic_exit_notifier.list), .priority = 0 }; diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 8856102..2fa6147 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -85,7 +85,7 @@ static int tts_notify_reboot(struct notifier_block *this, static struct notifier_block tts_notifier = { .notifier_call = tts_notify_reboot, - .next = NULL, + .list = LIST_HEAD_INIT(tts_notifier.list), .priority = 0, }; diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 2c29942..5a49739 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -4473,7 +4473,7 @@ static int panic_event(struct notifier_block *this, static struct notifier_block panic_block = { .notifier_call = panic_event, - .next = NULL, + .list = LIST_HEAD_INIT(panic_block.list), .priority = 200 /* priority: INT_MAX >= x >= 0 */ }; diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 7ed356e..e340cc2 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1183,7 +1183,7 @@ static int wdog_reboot_handler(struct notifier_block *this, static struct notifier_block wdog_reboot_notifier = { .notifier_call = wdog_reboot_handler, - .next = NULL, + .list = LIST_HEAD_INIT(wdog_reboot_notifier.list), .priority = 0 }; @@ -1212,7 +1212,7 @@ static int wdog_panic_handler(struct notifier_block *this, static struct notifier_block wdog_panic_notifier = { .notifier_call = wdog_panic_handler, - .next = NULL, + .list = LIST_HEAD_INIT(wdog_panic_notifier.list), .priority = 150 /* priority: INT_MAX >= x >= 0 */ }; diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index ea5ac2d..fcd2334 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -505,7 +505,7 @@ static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, static struct notifier_block dcdbas_reboot_nb = { .notifier_call = dcdbas_reboot_notify, - .next = NULL, + .list = LIST_HEAD_INIT(dcdbas_reboot_nb.list), .priority = INT_MIN }; diff --git a/drivers/md/md.c b/drivers/md/md.c index a4c219e..166c917 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -8437,7 +8437,7 @@ static int md_notify_reboot(struct notifier_block *this, static struct notifier_block md_notifier = { .notifier_call = md_notify_reboot, - .next = NULL, + .list = LIST_HEAD_INIT(md_notifier.list), .priority = INT_MAX, /* before any real devices */ }; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index dd3bfe8..8bd87bb 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -197,7 +197,7 @@ static void igb_shutdown(struct pci_dev *); static int igb_notify_dca(struct notifier_block *, unsigned long, void *); static struct notifier_block dca_notifier = { .notifier_call = igb_notify_dca, - .next = NULL, + .list = LIST_HEAD_INIT(dca_notifier.list), .priority = 0 }; #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 18ca3bc..2f460ff 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -124,7 +124,7 @@ static int ixgbe_notify_dca(struct notifier_block *, unsigned long event, void *p); static struct notifier_block dca_notifier = { .notifier_call = ixgbe_notify_dca, - .next = NULL, + .list = LIST_HEAD_INIT(dca_notifier.list), .priority = 0 }; #endif diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 90153fc..9514d81 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -4197,7 +4197,7 @@ myri10ge_notify_dca(struct notifier_block *nb, unsigned long event, void *p) static struct notifier_block myri10ge_dca_notifier = { .notifier_call = myri10ge_notify_dca, - .next = NULL, + .list = LIST_HEAD_INIT(myri10ge_dca_notifier.list), .priority = 0, }; #endif /* CONFIG_MYRI10GE_DCA */ diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index 3e8283c..c71c2a5 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -313,7 +313,7 @@ static int viawget_suspend(struct pci_dev *pcid, pm_message_t state); static int viawget_resume(struct pci_dev *pcid); struct notifier_block device_notifier = { .notifier_call = device_notify_reboot, - .next = NULL, + .list = LIST_HEAD_INIT(device_notifier.list), .priority = 0, }; #endif diff --git a/include/linux/notifier.h b/include/linux/notifier.h index d65746e..67f9a3a 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -49,40 +49,40 @@ struct notifier_block { int (*notifier_call)(struct notifier_block *, unsigned long, void *); - struct notifier_block __rcu *next; + struct list_head list; int priority; }; struct atomic_notifier_head { spinlock_t lock; - struct notifier_block __rcu *head; + struct list_head list; }; struct blocking_notifier_head { struct rw_semaphore rwsem; - struct notifier_block __rcu *head; + struct list_head list; }; struct raw_notifier_head { - struct notifier_block __rcu *head; + struct list_head list; }; struct srcu_notifier_head { struct mutex mutex; struct srcu_struct srcu; - struct notifier_block __rcu *head; + struct list_head list; }; #define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \ spin_lock_init(&(name)->lock); \ - (name)->head = NULL; \ + INIT_LIST_HEAD(&(name)->list); \ } while (0) #define BLOCKING_INIT_NOTIFIER_HEAD(name) do { \ init_rwsem(&(name)->rwsem); \ - (name)->head = NULL; \ + INIT_LIST_HEAD(&(name)->list); \ } while (0) #define RAW_INIT_NOTIFIER_HEAD(name) do { \ - (name)->head = NULL; \ + INIT_LIST_HEAD(&(name)->list); \ } while (0) /* srcu_notifier_heads must be initialized and cleaned up dynamically */ @@ -92,12 +92,13 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); #define ATOMIC_NOTIFIER_INIT(name) { \ .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ - .head = NULL } + .list = LIST_HEAD_INIT(name.list) } #define BLOCKING_NOTIFIER_INIT(name) { \ .rwsem = __RWSEM_INITIALIZER((name).rwsem), \ - .head = NULL } + .list = LIST_HEAD_INIT(name.list) } #define RAW_NOTIFIER_INIT(name) { \ - .head = NULL } + .list = LIST_HEAD_INIT(name.list) } + /* srcu_notifier_heads cannot be initialized statically */ #define ATOMIC_NOTIFIER_HEAD(name) \ diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 0557f24..9ac45ba 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -805,7 +805,7 @@ done: static struct notifier_block dbg_reboot_notifier = { .notifier_call = dbg_notify_reboot, - .next = NULL, + .list = LIST_HEAD_INIT(dbg_reboot_notifier.list), .priority = INT_MAX, }; diff --git a/kernel/notifier.c b/kernel/notifier.c index 2d5cc4c..ad6feab 100644 --- a/kernel/notifier.c +++ b/kernel/notifier.c @@ -18,44 +18,48 @@ BLOCKING_NOTIFIER_HEAD(reboot_notifier_list); * are layered on top of these, with appropriate locking added. */ -static int notifier_chain_register(struct notifier_block **nl, +static int notifier_chain_register(struct list_head *nl, struct notifier_block *n) { - while ((*nl) != NULL) { - if (n->priority > (*nl)->priority) + struct notifier_block *cur; + + list_for_each_entry(cur, nl, list) { + if (n->priority > cur->priority) break; - nl = &((*nl)->next); } - n->next = *nl; - rcu_assign_pointer(*nl, n); + + list_add_tail_rcu(&n->list, &cur->list); return 0; } -static int notifier_chain_cond_register(struct notifier_block **nl, +static int notifier_chain_cond_register(struct list_head *nl, struct notifier_block *n) { - while ((*nl) != NULL) { - if ((*nl) == n) + struct notifier_block *cur; + + list_for_each_entry(cur, nl, list) { + if (cur == n) return 0; - if (n->priority > (*nl)->priority) + if (n->priority > cur->priority) break; - nl = &((*nl)->next); } - n->next = *nl; - rcu_assign_pointer(*nl, n); + + list_add_tail_rcu(&n->list, &cur->list); return 0; } -static int notifier_chain_unregister(struct notifier_block **nl, +static int notifier_chain_unregister(struct list_head *nl, struct notifier_block *n) { - while ((*nl) != NULL) { - if ((*nl) == n) { - rcu_assign_pointer(*nl, n->next); + struct notifier_block *cur; + + list_for_each_entry(cur, nl, list) { + if (cur == n) { + list_del_bidir_rcu(&cur->list); return 0; } - nl = &((*nl)->next); } + return -ENOENT; } @@ -71,22 +75,20 @@ static int notifier_chain_unregister(struct notifier_block **nl, * @returns: notifier_call_chain returns the value returned by the * last notifier function called. */ -static int __kprobes notifier_call_chain(struct notifier_block **nl, +static int __kprobes notifier_call_chain(struct list_head *nl, unsigned long val, void *v, int nr_to_call, int *nr_calls) { int ret = NOTIFY_DONE; - struct notifier_block *nb, *next_nb; - - nb = rcu_dereference_raw(*nl); + struct notifier_block *nb; - while (nb && nr_to_call) { - next_nb = rcu_dereference_raw(nb->next); + list_for_each_entry_rcu(nb, nl, list) { + if (!nr_to_call) + break; #ifdef CONFIG_DEBUG_NOTIFIERS if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) { WARN(1, "Invalid notifier called!"); - nb = next_nb; continue; } #endif @@ -97,9 +99,9 @@ static int __kprobes notifier_call_chain(struct notifier_block **nl, if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) break; - nb = next_nb; nr_to_call--; } + return ret; } @@ -124,7 +126,7 @@ int atomic_notifier_chain_register(struct atomic_notifier_head *nh, int ret; spin_lock_irqsave(&nh->lock, flags); - ret = notifier_chain_register(&nh->head, n); + ret = notifier_chain_register(&nh->list, n); spin_unlock_irqrestore(&nh->lock, flags); return ret; } @@ -146,7 +148,7 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, int ret; spin_lock_irqsave(&nh->lock, flags); - ret = notifier_chain_unregister(&nh->head, n); + ret = notifier_chain_unregister(&nh->list, n); spin_unlock_irqrestore(&nh->lock, flags); synchronize_rcu(); return ret; @@ -179,7 +181,7 @@ int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh, int ret; rcu_read_lock(); - ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); + ret = notifier_call_chain(&nh->list, val, v, nr_to_call, nr_calls); rcu_read_unlock(); return ret; } @@ -218,10 +220,10 @@ int blocking_notifier_chain_register(struct blocking_notifier_head *nh, * such times we must not call down_write(). */ if (unlikely(system_state == SYSTEM_BOOTING)) - return notifier_chain_register(&nh->head, n); + return notifier_chain_register(&nh->list, n); down_write(&nh->rwsem); - ret = notifier_chain_register(&nh->head, n); + ret = notifier_chain_register(&nh->list, n); up_write(&nh->rwsem); return ret; } @@ -244,7 +246,7 @@ int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, int ret; down_write(&nh->rwsem); - ret = notifier_chain_cond_register(&nh->head, n); + ret = notifier_chain_cond_register(&nh->list, n); up_write(&nh->rwsem); return ret; } @@ -271,10 +273,10 @@ int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, * such times we must not call down_write(). */ if (unlikely(system_state == SYSTEM_BOOTING)) - return notifier_chain_unregister(&nh->head, n); + return notifier_chain_unregister(&nh->list, n); down_write(&nh->rwsem); - ret = notifier_chain_unregister(&nh->head, n); + ret = notifier_chain_unregister(&nh->list, n); up_write(&nh->rwsem); return ret; } @@ -305,13 +307,14 @@ int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, int ret = NOTIFY_DONE; /* - * We check the head outside the lock, but if this access is - * racy then it does not matter what the result of the test - * is, we re-check the list after having taken the lock anyway: + * We check whether the list is empty outside the lock, but if + * this access is racy then it does not matter what the result + * of the test is, we re-check the list after having taken the + * lock anyway. */ - if (rcu_dereference_raw(nh->head)) { + if (!list_empty(&nh->list)) { down_read(&nh->rwsem); - ret = notifier_call_chain(&nh->head, val, v, nr_to_call, + ret = notifier_call_chain(&nh->list, val, v, nr_to_call, nr_calls); up_read(&nh->rwsem); } @@ -344,7 +347,7 @@ EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n) { - return notifier_chain_register(&nh->head, n); + return notifier_chain_register(&nh->list, n); } EXPORT_SYMBOL_GPL(raw_notifier_chain_register); @@ -361,7 +364,7 @@ EXPORT_SYMBOL_GPL(raw_notifier_chain_register); int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *n) { - return notifier_chain_unregister(&nh->head, n); + return notifier_chain_unregister(&nh->list, n); } EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister); @@ -388,7 +391,7 @@ int __raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls) { - return notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); + return notifier_call_chain(&nh->list, val, v, nr_to_call, nr_calls); } EXPORT_SYMBOL_GPL(__raw_notifier_call_chain); @@ -425,10 +428,10 @@ int srcu_notifier_chain_register(struct srcu_notifier_head *nh, * such times we must not call mutex_lock(). */ if (unlikely(system_state == SYSTEM_BOOTING)) - return notifier_chain_register(&nh->head, n); + return notifier_chain_register(&nh->list, n); mutex_lock(&nh->mutex); - ret = notifier_chain_register(&nh->head, n); + ret = notifier_chain_register(&nh->list, n); mutex_unlock(&nh->mutex); return ret; } @@ -455,10 +458,10 @@ int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, * such times we must not call mutex_lock(). */ if (unlikely(system_state == SYSTEM_BOOTING)) - return notifier_chain_unregister(&nh->head, n); + return notifier_chain_unregister(&nh->list, n); mutex_lock(&nh->mutex); - ret = notifier_chain_unregister(&nh->head, n); + ret = notifier_chain_unregister(&nh->list, n); mutex_unlock(&nh->mutex); synchronize_srcu(&nh->srcu); return ret; @@ -491,7 +494,7 @@ int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, int idx; idx = srcu_read_lock(&nh->srcu); - ret = notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls); + ret = notifier_call_chain(&nh->list, val, v, nr_to_call, nr_calls); srcu_read_unlock(&nh->srcu, idx); return ret; } @@ -521,7 +524,7 @@ void srcu_init_notifier_head(struct srcu_notifier_head *nh) mutex_init(&nh->mutex); if (init_srcu_struct(&nh->srcu) < 0) BUG(); - nh->head = NULL; + INIT_LIST_HEAD(&nh->list); } EXPORT_SYMBOL_GPL(srcu_init_notifier_head); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index a7fa070..9feadb8 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4876,7 +4876,7 @@ static int trace_panic_handler(struct notifier_block *this, static struct notifier_block trace_panic_notifier = { .notifier_call = trace_panic_handler, - .next = NULL, + .list = LIST_HEAD_INIT(trace_panic_notifier.list), .priority = 150 /* priority: INT_MAX >= x >= 0 */ }; diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 93d8d2f..4135c16 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1583,7 +1583,7 @@ ratelimit_handler(struct notifier_block *self, unsigned long u, void *v) static struct notifier_block __cpuinitdata ratelimit_nb = { .notifier_call = ratelimit_handler, - .next = NULL, + .list = LIST_HEAD_INIT(ratelimit_nb.list), }; /* diff --git a/mm/vmstat.c b/mm/vmstat.c index 1bbbbd9..ad67df0 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1196,8 +1196,8 @@ static int __cpuinit vmstat_cpuup_callback(struct notifier_block *nfb, return NOTIFY_OK; } -static struct notifier_block __cpuinitdata vmstat_notifier = - { &vmstat_cpuup_callback, NULL, 0 }; +static struct notifier_block __cpuinitdata vmstat_notifier = { + &vmstat_cpuup_callback, LIST_HEAD_INIT(vmstat_notifier.list), 0 }; #endif static int __init setup_vmstat(void) -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/