Loading drivers/soc/qcom/subsystem_notif.c +117 −0 Original line number Diff line number Diff line Loading @@ -21,10 +21,25 @@ #include <linux/slab.h> #include <soc/qcom/subsystem_notif.h> /** * The callbacks that are registered in this data structure as early * notification callbacks will be called as soon as the SSR framework is * informed that the subsystem has crashed. This means that these functions will * be invoked as part of an IRQ handler, and thus, will be called in an atomic * context. Therefore, functions that are registered as early notification * callback must obey to the same constraints as interrupt handlers * (i.e. these functions must not sleep or block, etc). */ struct subsys_early_notif_info { spinlock_t cb_lock; void (*early_notif_cb[NUM_EARLY_NOTIFS])(void *); void *data[NUM_EARLY_NOTIFS]; }; struct subsys_notif_info { char name[50]; struct srcu_notifier_head subsys_notif_rcvr_list; struct subsys_early_notif_info early_notif_info; struct list_head list; }; Loading Loading @@ -94,6 +109,105 @@ int subsys_notif_unregister_notifier(void *subsys_handle, } EXPORT_SYMBOL(subsys_notif_unregister_notifier); void send_early_notifications(void *early_notif_handle) { struct subsys_early_notif_info *early_info = early_notif_handle; unsigned long flags; unsigned int i; void (*notif_cb)(void *data); if (!early_notif_handle) return; spin_lock_irqsave(&early_info->cb_lock, flags); for (i = 0; i < NUM_EARLY_NOTIFS; i++) { notif_cb = early_info->early_notif_cb[i]; if (notif_cb) notif_cb(early_info->data[i]); } spin_unlock_irqrestore(&early_info->cb_lock, flags); } EXPORT_SYMBOL(send_early_notifications); static bool valid_early_notif(enum early_subsys_notif_type notif_type) { return notif_type >= 0 && notif_type < NUM_EARLY_NOTIFS; } /** * The early_notif_cb parameter must point to a function that conforms to the * same constraints placed upon interrupt handlers, as the function will be * called in an atomic context (i.e. these functions must not sleep or block). */ int subsys_register_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type, void (*early_notif_cb)(void *), void *data) { struct subsys_notif_info *subsys; struct subsys_early_notif_info *early_notif_info; unsigned long flags; int rc = 0; if (!subsys_name || !early_notif_cb || !valid_early_notif(notif_type)) return -EINVAL; subsys = _notif_find_subsys(subsys_name); if (!subsys) return -EINVAL; early_notif_info = &subsys->early_notif_info; spin_lock_irqsave(&early_notif_info->cb_lock, flags); if (early_notif_info->early_notif_cb[notif_type]) { rc = -EEXIST; goto out; } early_notif_info->early_notif_cb[notif_type] = early_notif_cb; early_notif_info->data[notif_type] = data; out: spin_unlock_irqrestore(&early_notif_info->cb_lock, flags); return rc; } EXPORT_SYMBOL(subsys_register_early_notifier); int subsys_unregister_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type) { struct subsys_notif_info *subsys; struct subsys_early_notif_info *early_notif_info; unsigned long flags; if (!subsys_name || !valid_early_notif(notif_type)) return -EINVAL; subsys = _notif_find_subsys(subsys_name); if (!subsys) return -EINVAL; early_notif_info = &subsys->early_notif_info; spin_lock_irqsave(&early_notif_info->cb_lock, flags); early_notif_info->early_notif_cb[notif_type] = NULL; early_notif_info->data[notif_type] = NULL; spin_unlock_irqrestore(&early_notif_info->cb_lock, flags); return 0; } EXPORT_SYMBOL(subsys_unregister_early_notifier); void *subsys_get_early_notif_info(const char *subsys_name) { struct subsys_notif_info *subsys; if (!subsys_name) return ERR_PTR(-EINVAL); subsys = _notif_find_subsys(subsys_name); if (!subsys) return ERR_PTR(-EINVAL); return &subsys->early_notif_info; } EXPORT_SYMBOL(subsys_get_early_notif_info); void *subsys_notif_add_subsys(const char *subsys_name) { struct subsys_notif_info *subsys = NULL; Loading Loading @@ -121,6 +235,9 @@ void *subsys_notif_add_subsys(const char *subsys_name) srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list); memset(&subsys->early_notif_info, 0, sizeof(struct subsys_early_notif_info)); spin_lock_init(&subsys->early_notif_info.cb_lock); INIT_LIST_HEAD(&subsys->list); mutex_lock(¬if_lock); Loading drivers/soc/qcom/subsystem_restart.c +4 −0 Original line number Diff line number Diff line Loading @@ -187,6 +187,7 @@ struct subsys_device { struct subsys_tracking track; void *notify; void *early_notify; struct device dev; struct module *owner; int count; Loading Loading @@ -1218,6 +1219,8 @@ int subsystem_restart_dev(struct subsys_device *dev) name = dev->desc->name; send_early_notifications(dev->early_notify); /* * If a system reboot/shutdown is underway, ignore subsystem errors. * However, print a message so that we know that a subsystem behaved Loading Loading @@ -1792,6 +1795,7 @@ struct subsys_device *subsys_register(struct subsys_desc *desc) sizeof(subsys->desc->fw_name)); subsys->notify = subsys_notif_add_subsys(desc->name); subsys->early_notify = subsys_get_early_notif_info(desc->name); snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name); wakeup_source_init(&subsys->ssr_wlock, subsys->wlname); Loading include/soc/qcom/subsystem_notif.h +37 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,11 @@ enum subsys_notif_type { SUBSYS_NOTIF_TYPE_COUNT }; enum early_subsys_notif_type { XPORT_LAYER_NOTIF, NUM_EARLY_NOTIFS }; #if defined(CONFIG_MSM_SUBSYSTEM_RESTART) /* Use the subsys_notif_register_notifier API to register for notifications for * a particular subsystem. This API will return a handle that can be used to Loading @@ -50,6 +55,14 @@ void *subsys_notif_add_subsys(const char *subsys_name); int subsys_notif_queue_notification(void *subsys_handle, enum subsys_notif_type notif_type, void *data); void *subsys_get_early_notif_info(const char *subsys_name); int subsys_register_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type, void (*early_notif_cb)(void *), void *data); int subsys_unregister_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type); void send_early_notifications(void *early_notif_handle); #else static inline void *subsys_notif_register_notifier( Loading @@ -75,6 +88,30 @@ static inline int subsys_notif_queue_notification(void *subsys_handle, { return 0; } static inline void *subsys_get_early_notif_info(const char *subsys_name) { return NULL; } static inline int subsys_register_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type, void (*early_notif_cb)(void *), void *data) { return -ENOTSUPP; } static inline int subsys_unregister_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type) { return -ENOTSUPP; } static inline void send_early_notifications(void *early_notif_handle) { } #endif /* CONFIG_MSM_SUBSYSTEM_RESTART */ #endif Loading
drivers/soc/qcom/subsystem_notif.c +117 −0 Original line number Diff line number Diff line Loading @@ -21,10 +21,25 @@ #include <linux/slab.h> #include <soc/qcom/subsystem_notif.h> /** * The callbacks that are registered in this data structure as early * notification callbacks will be called as soon as the SSR framework is * informed that the subsystem has crashed. This means that these functions will * be invoked as part of an IRQ handler, and thus, will be called in an atomic * context. Therefore, functions that are registered as early notification * callback must obey to the same constraints as interrupt handlers * (i.e. these functions must not sleep or block, etc). */ struct subsys_early_notif_info { spinlock_t cb_lock; void (*early_notif_cb[NUM_EARLY_NOTIFS])(void *); void *data[NUM_EARLY_NOTIFS]; }; struct subsys_notif_info { char name[50]; struct srcu_notifier_head subsys_notif_rcvr_list; struct subsys_early_notif_info early_notif_info; struct list_head list; }; Loading Loading @@ -94,6 +109,105 @@ int subsys_notif_unregister_notifier(void *subsys_handle, } EXPORT_SYMBOL(subsys_notif_unregister_notifier); void send_early_notifications(void *early_notif_handle) { struct subsys_early_notif_info *early_info = early_notif_handle; unsigned long flags; unsigned int i; void (*notif_cb)(void *data); if (!early_notif_handle) return; spin_lock_irqsave(&early_info->cb_lock, flags); for (i = 0; i < NUM_EARLY_NOTIFS; i++) { notif_cb = early_info->early_notif_cb[i]; if (notif_cb) notif_cb(early_info->data[i]); } spin_unlock_irqrestore(&early_info->cb_lock, flags); } EXPORT_SYMBOL(send_early_notifications); static bool valid_early_notif(enum early_subsys_notif_type notif_type) { return notif_type >= 0 && notif_type < NUM_EARLY_NOTIFS; } /** * The early_notif_cb parameter must point to a function that conforms to the * same constraints placed upon interrupt handlers, as the function will be * called in an atomic context (i.e. these functions must not sleep or block). */ int subsys_register_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type, void (*early_notif_cb)(void *), void *data) { struct subsys_notif_info *subsys; struct subsys_early_notif_info *early_notif_info; unsigned long flags; int rc = 0; if (!subsys_name || !early_notif_cb || !valid_early_notif(notif_type)) return -EINVAL; subsys = _notif_find_subsys(subsys_name); if (!subsys) return -EINVAL; early_notif_info = &subsys->early_notif_info; spin_lock_irqsave(&early_notif_info->cb_lock, flags); if (early_notif_info->early_notif_cb[notif_type]) { rc = -EEXIST; goto out; } early_notif_info->early_notif_cb[notif_type] = early_notif_cb; early_notif_info->data[notif_type] = data; out: spin_unlock_irqrestore(&early_notif_info->cb_lock, flags); return rc; } EXPORT_SYMBOL(subsys_register_early_notifier); int subsys_unregister_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type) { struct subsys_notif_info *subsys; struct subsys_early_notif_info *early_notif_info; unsigned long flags; if (!subsys_name || !valid_early_notif(notif_type)) return -EINVAL; subsys = _notif_find_subsys(subsys_name); if (!subsys) return -EINVAL; early_notif_info = &subsys->early_notif_info; spin_lock_irqsave(&early_notif_info->cb_lock, flags); early_notif_info->early_notif_cb[notif_type] = NULL; early_notif_info->data[notif_type] = NULL; spin_unlock_irqrestore(&early_notif_info->cb_lock, flags); return 0; } EXPORT_SYMBOL(subsys_unregister_early_notifier); void *subsys_get_early_notif_info(const char *subsys_name) { struct subsys_notif_info *subsys; if (!subsys_name) return ERR_PTR(-EINVAL); subsys = _notif_find_subsys(subsys_name); if (!subsys) return ERR_PTR(-EINVAL); return &subsys->early_notif_info; } EXPORT_SYMBOL(subsys_get_early_notif_info); void *subsys_notif_add_subsys(const char *subsys_name) { struct subsys_notif_info *subsys = NULL; Loading Loading @@ -121,6 +235,9 @@ void *subsys_notif_add_subsys(const char *subsys_name) srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list); memset(&subsys->early_notif_info, 0, sizeof(struct subsys_early_notif_info)); spin_lock_init(&subsys->early_notif_info.cb_lock); INIT_LIST_HEAD(&subsys->list); mutex_lock(¬if_lock); Loading
drivers/soc/qcom/subsystem_restart.c +4 −0 Original line number Diff line number Diff line Loading @@ -187,6 +187,7 @@ struct subsys_device { struct subsys_tracking track; void *notify; void *early_notify; struct device dev; struct module *owner; int count; Loading Loading @@ -1218,6 +1219,8 @@ int subsystem_restart_dev(struct subsys_device *dev) name = dev->desc->name; send_early_notifications(dev->early_notify); /* * If a system reboot/shutdown is underway, ignore subsystem errors. * However, print a message so that we know that a subsystem behaved Loading Loading @@ -1792,6 +1795,7 @@ struct subsys_device *subsys_register(struct subsys_desc *desc) sizeof(subsys->desc->fw_name)); subsys->notify = subsys_notif_add_subsys(desc->name); subsys->early_notify = subsys_get_early_notif_info(desc->name); snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name); wakeup_source_init(&subsys->ssr_wlock, subsys->wlname); Loading
include/soc/qcom/subsystem_notif.h +37 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,11 @@ enum subsys_notif_type { SUBSYS_NOTIF_TYPE_COUNT }; enum early_subsys_notif_type { XPORT_LAYER_NOTIF, NUM_EARLY_NOTIFS }; #if defined(CONFIG_MSM_SUBSYSTEM_RESTART) /* Use the subsys_notif_register_notifier API to register for notifications for * a particular subsystem. This API will return a handle that can be used to Loading @@ -50,6 +55,14 @@ void *subsys_notif_add_subsys(const char *subsys_name); int subsys_notif_queue_notification(void *subsys_handle, enum subsys_notif_type notif_type, void *data); void *subsys_get_early_notif_info(const char *subsys_name); int subsys_register_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type, void (*early_notif_cb)(void *), void *data); int subsys_unregister_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type); void send_early_notifications(void *early_notif_handle); #else static inline void *subsys_notif_register_notifier( Loading @@ -75,6 +88,30 @@ static inline int subsys_notif_queue_notification(void *subsys_handle, { return 0; } static inline void *subsys_get_early_notif_info(const char *subsys_name) { return NULL; } static inline int subsys_register_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type, void (*early_notif_cb)(void *), void *data) { return -ENOTSUPP; } static inline int subsys_unregister_early_notifier(const char *subsys_name, enum early_subsys_notif_type notif_type) { return -ENOTSUPP; } static inline void send_early_notifications(void *early_notif_handle) { } #endif /* CONFIG_MSM_SUBSYSTEM_RESTART */ #endif