Commit 5abfe5cf authored by Rishabh Bhatnagar's avatar Rishabh Bhatnagar Committed by Bjorn Andersson

remoteproc: qcom: Add per subsystem SSR notification

Currently there is a single notification chain which is called whenever any
remoteproc shuts down. This leads to all the listeners being notified, and
is not an optimal design as kernel drivers might only be interested in
listening to notifications from a particular remoteproc. Create a global
list of remoteproc notification info data structures. This will hold the
name and notifier_list information for a particular remoteproc. The API
to register for notifications will use name argument to retrieve the
notification info data structure and the notifier block will be added to
that data structure's notification chain. Also move from blocking notifier
to srcu notifer based implementation to support dynamic notifier head
creation.
Reviewed-by: default avatarAlex Elder <elder@linaro.org>
Co-developed-by: default avatarSiddharth Gupta <sidgup@codeaurora.org>
Signed-off-by: default avatarSiddharth Gupta <sidgup@codeaurora.org>
Signed-off-by: default avatarRishabh Bhatnagar <rishabhb@codeaurora.org>
Link: https://lore.kernel.org/r/1592965408-16908-2-git-send-email-rishabhb@codeaurora.orgSigned-off-by: default avatarBjorn Andersson <bjorn.andersson@linaro.org>
parent d4c78d21
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/remoteproc.h> #include <linux/remoteproc.h>
#include <linux/remoteproc/qcom_rproc.h>
#include <linux/rpmsg/qcom_glink.h> #include <linux/rpmsg/qcom_glink.h>
#include <linux/rpmsg/qcom_smd.h> #include <linux/rpmsg/qcom_smd.h>
#include <linux/soc/qcom/mdt_loader.h> #include <linux/soc/qcom/mdt_loader.h>
...@@ -23,7 +24,14 @@ ...@@ -23,7 +24,14 @@
#define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
#define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev) #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); struct qcom_ssr_subsystem {
const char *name;
struct srcu_notifier_head notifier_list;
struct list_head list;
};
static LIST_HEAD(qcom_ssr_subsystem_list);
static DEFINE_MUTEX(qcom_ssr_subsys_lock);
static int glink_subdev_start(struct rproc_subdev *subdev) static int glink_subdev_start(struct rproc_subdev *subdev)
{ {
...@@ -189,37 +197,83 @@ void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) ...@@ -189,37 +197,83 @@ void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
} }
EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev); EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name)
{
struct qcom_ssr_subsystem *info;
mutex_lock(&qcom_ssr_subsys_lock);
/* Match in the global qcom_ssr_subsystem_list with name */
list_for_each_entry(info, &qcom_ssr_subsystem_list, list)
if (!strcmp(info->name, name))
goto out;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
info = ERR_PTR(-ENOMEM);
goto out;
}
info->name = kstrdup_const(name, GFP_KERNEL);
srcu_init_notifier_head(&info->notifier_list);
/* Add to global notification list */
list_add_tail(&info->list, &qcom_ssr_subsystem_list);
out:
mutex_unlock(&qcom_ssr_subsys_lock);
return info;
}
/** /**
* qcom_register_ssr_notifier() - register SSR notification handler * qcom_register_ssr_notifier() - register SSR notification handler
* @nb: notifier_block to notify for restart notifications * @name: Subsystem's SSR name
* @nb: notifier_block to be invoked upon subsystem's state change
* *
* Returns 0 on success, negative errno on failure. * This registers the @nb notifier block as part the notifier chain for a
* remoteproc associated with @name. The notifier block's callback
* will be invoked when the remote processor's SSR events occur
* (pre/post startup and pre/post shutdown).
* *
* This register the @notify function as handler for restart notifications. As * Return: a subsystem cookie on success, ERR_PTR on failure.
* remote processors are stopped this function will be called, with the SSR
* name passed as a parameter.
*/ */
int qcom_register_ssr_notifier(struct notifier_block *nb) void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb)
{ {
return blocking_notifier_chain_register(&ssr_notifiers, nb); struct qcom_ssr_subsystem *info;
info = qcom_ssr_get_subsys(name);
if (IS_ERR(info))
return info;
srcu_notifier_chain_register(&info->notifier_list, nb);
return &info->notifier_list;
} }
EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier); EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
/** /**
* qcom_unregister_ssr_notifier() - unregister SSR notification handler * qcom_unregister_ssr_notifier() - unregister SSR notification handler
* @notify: subsystem cookie returned from qcom_register_ssr_notifier
* @nb: notifier_block to unregister * @nb: notifier_block to unregister
*
* This function will unregister the notifier from the particular notifier
* chain.
*
* Return: 0 on success, %ENOENT otherwise.
*/ */
void qcom_unregister_ssr_notifier(struct notifier_block *nb) int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb)
{ {
blocking_notifier_chain_unregister(&ssr_notifiers, nb); return srcu_notifier_chain_unregister(notify, nb);
} }
EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier); EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
static void ssr_notify_unprepare(struct rproc_subdev *subdev) static void ssr_notify_unprepare(struct rproc_subdev *subdev)
{ {
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
struct qcom_ssr_notify_data data = {
.name = ssr->info->name,
.crashed = false,
};
blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr->name); srcu_notifier_call_chain(&ssr->info->notifier_list, 0, &data);
} }
/** /**
...@@ -229,12 +283,21 @@ static void ssr_notify_unprepare(struct rproc_subdev *subdev) ...@@ -229,12 +283,21 @@ static void ssr_notify_unprepare(struct rproc_subdev *subdev)
* @ssr_name: identifier to use for notifications originating from @rproc * @ssr_name: identifier to use for notifications originating from @rproc
* *
* As the @ssr is registered with the @rproc SSR events will be sent to all * As the @ssr is registered with the @rproc SSR events will be sent to all
* registered listeners in the system as the remoteproc is shut down. * registered listeners for the remoteproc when it's SSR events occur
* (pre/post startup and pre/post shutdown).
*/ */
void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
const char *ssr_name) const char *ssr_name)
{ {
ssr->name = ssr_name; struct qcom_ssr_subsystem *info;
info = qcom_ssr_get_subsys(ssr_name);
if (IS_ERR(info)) {
dev_err(&rproc->dev, "Failed to add ssr subdevice\n");
return;
}
ssr->info = info;
ssr->subdev.unprepare = ssr_notify_unprepare; ssr->subdev.unprepare = ssr_notify_unprepare;
rproc_add_subdev(rproc, &ssr->subdev); rproc_add_subdev(rproc, &ssr->subdev);
...@@ -249,6 +312,7 @@ EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev); ...@@ -249,6 +312,7 @@ EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr) void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
{ {
rproc_remove_subdev(rproc, &ssr->subdev); rproc_remove_subdev(rproc, &ssr->subdev);
ssr->info = NULL;
} }
EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev); EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
......
...@@ -26,10 +26,11 @@ struct qcom_rproc_subdev { ...@@ -26,10 +26,11 @@ struct qcom_rproc_subdev {
struct qcom_smd_edge *edge; struct qcom_smd_edge *edge;
}; };
struct qcom_ssr_subsystem;
struct qcom_rproc_ssr { struct qcom_rproc_ssr {
struct rproc_subdev subdev; struct rproc_subdev subdev;
struct qcom_ssr_subsystem *info;
const char *name;
}; };
void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink, void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
......
...@@ -5,17 +5,27 @@ struct notifier_block; ...@@ -5,17 +5,27 @@ struct notifier_block;
#if IS_ENABLED(CONFIG_QCOM_RPROC_COMMON) #if IS_ENABLED(CONFIG_QCOM_RPROC_COMMON)
int qcom_register_ssr_notifier(struct notifier_block *nb); struct qcom_ssr_notify_data {
void qcom_unregister_ssr_notifier(struct notifier_block *nb); const char *name;
bool crashed;
};
void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb);
int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb);
#else #else
static inline int qcom_register_ssr_notifier(struct notifier_block *nb) static inline void *qcom_register_ssr_notifier(const char *name,
struct notifier_block *nb)
{ {
return 0; return NULL;
} }
static inline void qcom_unregister_ssr_notifier(struct notifier_block *nb) {} static inline int qcom_unregister_ssr_notifier(void *notify,
struct notifier_block *nb)
{
return 0;
}
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment