diff --git a/drivers/soc/qcom/msm_glink_ssr.c b/drivers/soc/qcom/msm_glink_ssr.c index 491c2105adfd..2ba7aa017c39 100644 --- a/drivers/soc/qcom/msm_glink_ssr.c +++ b/drivers/soc/qcom/msm_glink_ssr.c @@ -16,6 +16,7 @@ #define MSM_SSR_LOG_PAGE_CNT 4 static void *ssr_ilc; +static DEFINE_MUTEX(ssr_lock); #define MSM_SSR_INFO(x, ...) ipc_log_string(ssr_ilc, x, ##__VA_ARGS__) @@ -64,8 +65,21 @@ struct glink_ssr { u32 seq_num; struct completion completion; struct work_struct unreg_work; + struct kref refcount; }; +static void glink_ssr_release(struct kref *ref) +{ + struct glink_ssr *ssr = container_of(ref, struct glink_ssr, + refcount); + struct glink_ssr_nb *nb, *tmp; + + list_for_each_entry_safe(nb, tmp, &ssr->notify_list, list) + kfree(nb); + + kfree(ssr); +} + static void glink_ssr_ssr_unreg_work(struct work_struct *work) { struct glink_ssr *ssr = container_of(work, struct glink_ssr, @@ -75,9 +89,8 @@ static void glink_ssr_ssr_unreg_work(struct work_struct *work) 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); + kref_put(&ssr->refcount, glink_ssr_release); } static int glink_ssr_ssr_cb(struct notifier_block *this, @@ -85,12 +98,15 @@ static int glink_ssr_ssr_cb(struct notifier_block *this, { struct glink_ssr_nb *nb = container_of(this, struct glink_ssr_nb, nb); struct glink_ssr *ssr = nb->ssr; - struct device *dev = ssr->dev; + struct device *dev; struct do_cleanup_msg msg; int ret; + kref_get(&ssr->refcount); + mutex_lock(&ssr_lock); + dev = ssr->dev; if (!dev || !ssr->ept) - return NOTIFY_DONE; + goto out; if (code == SUBSYS_AFTER_SHUTDOWN || code == SUBSYS_POWERUP_FAILURE) { ssr->seq_num++; @@ -110,13 +126,16 @@ static int glink_ssr_ssr_cb(struct notifier_block *this, if (ret) { MSM_SSR_ERR(dev, "fail to send do cleanup to %s %d\n", nb->ssr_label, ret); - return NOTIFY_DONE; + goto out; } ret = wait_for_completion_timeout(&ssr->completion, HZ); if (!ret) MSM_SSR_ERR(dev, "timeout waiting for cleanup resp\n"); } +out: + mutex_unlock(&ssr_lock); + kref_put(&ssr->refcount, glink_ssr_release); return NOTIFY_DONE; } @@ -217,6 +236,7 @@ static int glink_ssr_probe(struct rpmsg_device *rpdev) INIT_LIST_HEAD(&ssr->notify_list); init_completion(&ssr->completion); INIT_WORK(&ssr->unreg_work, glink_ssr_ssr_unreg_work); + kref_init(&ssr->refcount); ssr->dev = &rpdev->dev; ssr->ept = rpdev->ept; @@ -235,11 +255,12 @@ static void glink_ssr_remove(struct rpmsg_device *rpdev) { struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev); + mutex_lock(&ssr_lock); ssr->dev = NULL; ssr->ept = NULL; + mutex_unlock(&ssr_lock); dev_set_drvdata(&rpdev->dev, NULL); - schedule_work(&ssr->unreg_work); }