Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8951570 authored by Arun Kumar Neelakantam's avatar Arun Kumar Neelakantam
Browse files

soc: qcom: glink_probe: Defer SSR unregister



Calling SSR unregister for other peripherals from glink_ssr device remove
call back results in deadlock if multiple SSRs happens in parallel.

SSR unregister is defer to other worker to avoid notifier SRCU deadlock.

Change-Id: Ifa5c461201eec4b4beb8bf405bd0a2904a2cc90f
Signed-off-by: default avatarArun Kumar Neelakantam <aneela@codeaurora.org>
parent 8f765160
Loading
Loading
Loading
Loading
+28 −9
Original line number Diff line number Diff line
/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2017-2019, 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
@@ -77,6 +77,7 @@ struct glink_ssr {

	u32 seq_num;
	struct completion completion;
	struct work_struct unreg_work;
};

struct edge_info {
@@ -94,6 +95,20 @@ struct edge_info {
};
LIST_HEAD(edge_infos);

static void glink_ssr_ssr_unreg_work(struct work_struct *work)
{
	struct glink_ssr *ssr = container_of(work, struct glink_ssr,
					     unreg_work);
	struct glink_ssr_nb *nb, *tmp;

	list_for_each_entry_safe(nb, tmp, &ssr->notify_list, list) {
		subsys_notif_unregister_notifier(nb->ssr_register_handle,
						 &nb->nb);
		kfree(nb);
	}
	kfree(ssr);
}

static int glink_ssr_ssr_cb(struct notifier_block *this,
			    unsigned long code, void *data)
{
@@ -103,6 +118,9 @@ static int glink_ssr_ssr_cb(struct notifier_block *this,
	struct do_cleanup_msg msg;
	int ret;

	if (!dev || !ssr->ept)
		return NOTIFY_DONE;

	if (code == SUBSYS_AFTER_SHUTDOWN) {
		ssr->seq_num++;
		reinit_completion(&ssr->completion);
@@ -179,7 +197,7 @@ static void glink_ssr_init_notify(struct glink_ssr *ssr)
		if (!node)
			break;

		nb = devm_kzalloc(dev, sizeof(*nb), GFP_KERNEL);
		nb = kzalloc(sizeof(*nb), GFP_KERNEL);
		if (!nb)
			return;

@@ -192,6 +210,7 @@ static void glink_ssr_init_notify(struct glink_ssr *ssr)
		if (ret < 0) {
			GLINK_ERR(dev, "no qcom,glink-label for %s\n",
				  nb->ssr_label);
			kfree(nb);
			continue;
		}

@@ -202,6 +221,7 @@ static void glink_ssr_init_notify(struct glink_ssr *ssr)
		if (IS_ERR_OR_NULL(handle)) {
			GLINK_ERR(dev, "register fail for %s SSR notifier\n",
				  nb->ssr_label);
			kfree(nb);
			continue;
		}

@@ -215,12 +235,13 @@ static int glink_ssr_probe(struct rpmsg_device *rpdev)
{
	struct glink_ssr *ssr;

	ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL);
	ssr = kzalloc(sizeof(*ssr), GFP_KERNEL);
	if (!ssr)
		return -ENOMEM;

	INIT_LIST_HEAD(&ssr->notify_list);
	init_completion(&ssr->completion);
	INIT_WORK(&ssr->unreg_work, glink_ssr_ssr_unreg_work);

	ssr->dev = &rpdev->dev;
	ssr->ept = rpdev->ept;
@@ -235,14 +256,12 @@ static int glink_ssr_probe(struct rpmsg_device *rpdev)
static void glink_ssr_remove(struct rpmsg_device *rpdev)
{
	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);
	struct glink_ssr_nb *nb;

	list_for_each_entry(nb, &ssr->notify_list, list) {
		subsys_notif_unregister_notifier(nb->ssr_register_handle,
						 &nb->nb);
	}

	ssr->dev = NULL;
	ssr->ept = NULL;
	dev_set_drvdata(&rpdev->dev, NULL);

	schedule_work(&ssr->unreg_work);
}

static const struct rpmsg_device_id glink_ssr_match[] = {