Loading drivers/rpmsg/qcom_glink_native.c +37 −12 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <linux/kthread.h> #include <linux/mailbox_client.h> #include <linux/ipc_logging.h> #include <soc/qcom/subsystem_notif.h> #include "rpmsg_internal.h" #include "qcom_glink_native.h" Loading Loading @@ -1828,6 +1829,23 @@ static void qcom_glink_set_affinity(struct qcom_glink *glink, u32 *arr, dev_err(glink->dev, "failed to set task affinity\n"); } static void qcom_glink_notif_reset(void *data) { struct qcom_glink *glink = data; struct glink_channel *channel; unsigned long flags; int cid; if (!glink) return; atomic_inc(&glink->in_reset); spin_lock_irqsave(&glink->idr_lock, flags); idr_for_each_entry(&glink->lcids, channel, cid) { wake_up(&channel->intent_req_event); } spin_unlock_irqrestore(&glink->idr_lock, flags); } struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, Loading Loading @@ -1886,6 +1904,13 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, return ERR_CAST(glink->task); } ret = subsys_register_early_notifier(glink->name, XPORT_LAYER_NOTIF, qcom_glink_notif_reset, glink); if (ret) { dev_err(dev, "failed to register early notif %d\n", ret); return ERR_PTR(ret); } irq = of_irq_get(dev->of_node, 0); ret = devm_request_irq(dev, irq, qcom_glink_native_intr, Loading @@ -1893,7 +1918,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, "glink-native", glink); if (ret) { dev_err(dev, "failed to request IRQ\n"); return ERR_PTR(ret); goto unregister; } glink->irq = irq; Loading @@ -1901,8 +1926,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, size = of_property_count_u32_elems(dev->of_node, "cpu-affinity"); if (size > 0) { arr = kmalloc_array(size, sizeof(u32), GFP_KERNEL); if (!arr) return ERR_PTR(-ENOMEM); if (!arr) { ret = -ENOMEM; goto unregister; } ret = of_property_read_u32_array(dev->of_node, "cpu-affinity", arr, size); if (!ret) Loading @@ -1913,7 +1940,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, ret = qcom_glink_send_version(glink); if (ret) { dev_err(dev, "failed to send version %d\n", ret); return ERR_PTR(ret); goto unregister; } ret = qcom_glink_create_chrdev(glink); Loading @@ -1923,6 +1950,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, glink->ilc = ipc_log_context_create(GLINK_LOG_PAGE_CNT, glink->name, 0); return glink; unregister: subsys_unregister_early_notifier(glink->name, XPORT_LAYER_NOTIF); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(qcom_glink_native_probe); Loading @@ -1940,17 +1971,11 @@ void qcom_glink_native_remove(struct qcom_glink *glink) int ret; unsigned long flags; atomic_inc(&glink->in_reset); subsys_unregister_early_notifier(glink->name, XPORT_LAYER_NOTIF); qcom_glink_notif_reset(glink); disable_irq(glink->irq); cancel_work_sync(&glink->rx_work); /* Signal all threads to cancel tx */ spin_lock_irqsave(&glink->idr_lock, flags); idr_for_each_entry(&glink->lcids, channel, cid) { wake_up(&channel->intent_req_event); } spin_unlock_irqrestore(&glink->idr_lock, flags); ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device); if (ret) dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret); Loading drivers/soc/qcom/subsystem_notif.c +118 −1 Original line number Diff line number Diff line /* Copyright (c) 2011, 2013, 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2011, 2013, 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -28,10 +28,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 @@ -101,6 +116,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 *); 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 @@ -128,6 +242,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 @@ -194,6 +194,7 @@ struct subsys_device { struct subsys_tracking track; void *notify; void *early_notify; struct device dev; struct module *owner; int count; Loading Loading @@ -1227,6 +1228,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 @@ -1801,6 +1804,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 +38 −1 Original line number Diff line number Diff line /* Copyright (c) 2011, 2013 - 2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2011, 2013 - 2014, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -32,6 +32,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 @@ -57,6 +62,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 @@ -82,6 +95,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/rpmsg/qcom_glink_native.c +37 −12 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <linux/kthread.h> #include <linux/mailbox_client.h> #include <linux/ipc_logging.h> #include <soc/qcom/subsystem_notif.h> #include "rpmsg_internal.h" #include "qcom_glink_native.h" Loading Loading @@ -1828,6 +1829,23 @@ static void qcom_glink_set_affinity(struct qcom_glink *glink, u32 *arr, dev_err(glink->dev, "failed to set task affinity\n"); } static void qcom_glink_notif_reset(void *data) { struct qcom_glink *glink = data; struct glink_channel *channel; unsigned long flags; int cid; if (!glink) return; atomic_inc(&glink->in_reset); spin_lock_irqsave(&glink->idr_lock, flags); idr_for_each_entry(&glink->lcids, channel, cid) { wake_up(&channel->intent_req_event); } spin_unlock_irqrestore(&glink->idr_lock, flags); } struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, Loading Loading @@ -1886,6 +1904,13 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, return ERR_CAST(glink->task); } ret = subsys_register_early_notifier(glink->name, XPORT_LAYER_NOTIF, qcom_glink_notif_reset, glink); if (ret) { dev_err(dev, "failed to register early notif %d\n", ret); return ERR_PTR(ret); } irq = of_irq_get(dev->of_node, 0); ret = devm_request_irq(dev, irq, qcom_glink_native_intr, Loading @@ -1893,7 +1918,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, "glink-native", glink); if (ret) { dev_err(dev, "failed to request IRQ\n"); return ERR_PTR(ret); goto unregister; } glink->irq = irq; Loading @@ -1901,8 +1926,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, size = of_property_count_u32_elems(dev->of_node, "cpu-affinity"); if (size > 0) { arr = kmalloc_array(size, sizeof(u32), GFP_KERNEL); if (!arr) return ERR_PTR(-ENOMEM); if (!arr) { ret = -ENOMEM; goto unregister; } ret = of_property_read_u32_array(dev->of_node, "cpu-affinity", arr, size); if (!ret) Loading @@ -1913,7 +1940,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, ret = qcom_glink_send_version(glink); if (ret) { dev_err(dev, "failed to send version %d\n", ret); return ERR_PTR(ret); goto unregister; } ret = qcom_glink_create_chrdev(glink); Loading @@ -1923,6 +1950,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev, glink->ilc = ipc_log_context_create(GLINK_LOG_PAGE_CNT, glink->name, 0); return glink; unregister: subsys_unregister_early_notifier(glink->name, XPORT_LAYER_NOTIF); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(qcom_glink_native_probe); Loading @@ -1940,17 +1971,11 @@ void qcom_glink_native_remove(struct qcom_glink *glink) int ret; unsigned long flags; atomic_inc(&glink->in_reset); subsys_unregister_early_notifier(glink->name, XPORT_LAYER_NOTIF); qcom_glink_notif_reset(glink); disable_irq(glink->irq); cancel_work_sync(&glink->rx_work); /* Signal all threads to cancel tx */ spin_lock_irqsave(&glink->idr_lock, flags); idr_for_each_entry(&glink->lcids, channel, cid) { wake_up(&channel->intent_req_event); } spin_unlock_irqrestore(&glink->idr_lock, flags); ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device); if (ret) dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret); Loading
drivers/soc/qcom/subsystem_notif.c +118 −1 Original line number Diff line number Diff line /* Copyright (c) 2011, 2013, 2016-2017, The Linux Foundation. All rights reserved. /* Copyright (c) 2011, 2013, 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -28,10 +28,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 @@ -101,6 +116,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 *); 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 @@ -128,6 +242,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 @@ -194,6 +194,7 @@ struct subsys_device { struct subsys_tracking track; void *notify; void *early_notify; struct device dev; struct module *owner; int count; Loading Loading @@ -1227,6 +1228,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 @@ -1801,6 +1804,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 +38 −1 Original line number Diff line number Diff line /* Copyright (c) 2011, 2013 - 2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2011, 2013 - 2014, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -32,6 +32,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 @@ -57,6 +62,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 @@ -82,6 +95,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