[v3,05/18] soc: qcom: minidump: Add pending region registration support

Message ID 1683133352-10046-6-git-send-email-quic_mojha@quicinc.com
State New
Headers
Series [v3,01/18] remoteproc: qcom: Expand MD_* as MINIDUMP_* |

Commit Message

Mukesh Ojha May 3, 2023, 5:02 p.m. UTC
  Pending regions are those apss minidump regions which came for
registration before minidump was initialized or ready to do
register the region.

We can add regions to pending region list and register them from
apss minidump driver probe in one go.

Signed-off-by: Mukesh Ojha <quic_mojha@quicinc.com>
---
 drivers/soc/qcom/qcom_minidump.c | 114 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 100 insertions(+), 14 deletions(-)
  

Patch

diff --git a/drivers/soc/qcom/qcom_minidump.c b/drivers/soc/qcom/qcom_minidump.c
index d107a86..6d29371 100644
--- a/drivers/soc/qcom/qcom_minidump.c
+++ b/drivers/soc/qcom/qcom_minidump.c
@@ -49,6 +49,25 @@  struct minidump {
 	struct device			*dev;
 };
 
+/**
+ * struct minidump_pending_region - Minidump pending region
+ * @list	: Pending region list pointer
+ * @region	: APSS minidump client region
+ */
+struct minidump_pending_region {
+	struct list_head			list;
+	struct qcom_apss_minidump_region	region;
+};
+
+/**
+ * struct minidump_pending_region_list - Minidump pending region list
+ * @pregion_list	: List of pending region to be registered
+ * @pregion_cnt		: Count of the pending region to be registered
+ */
+struct minidump_pending_region_list {
+	struct list_head	pregion_list;
+	int			pregion_cnt;
+};
 /*
  * In some of the Old Qualcomm devices, boot firmware statically allocates 300
  * as total number of supported region (including all co-processors) in
@@ -62,6 +81,10 @@  struct minidump {
 
 static struct minidump *__md;
 static DEFINE_MUTEX(minidump_lock);
+static struct minidump_pending_region_list md_pregion_list = {
+	.pregion_list = LIST_HEAD_INIT(md_pregion_list.pregion_list),
+	.pregion_cnt = 0,
+};
 
 static struct elf_shdr *elf_shdr_entry_addr(struct elfhdr *ehdr, int idx)
 {
@@ -312,6 +335,26 @@  struct minidump_subsystem *qcom_minidump_subsystem_desc(unsigned int minidump_in
 }
 EXPORT_SYMBOL_GPL(qcom_minidump_subsystem_desc);
 
+static struct minidump_pending_region *
+get_qcom_apss_pending_region(const struct qcom_apss_minidump_region *region)
+{
+	struct minidump_pending_region *md_pregion;
+	struct minidump_pending_region *tmp;
+	bool found = false;
+
+	list_for_each_entry_safe(md_pregion, tmp, &md_pregion_list.pregion_list, list) {
+		struct qcom_apss_minidump_region *md_region;
+
+		md_region = &md_pregion->region;
+		if (!strcmp(md_region->name, region->name)) {
+			found = true;
+			break;
+		}
+	}
+
+	return found ? md_pregion : NULL;
+}
+
 /**
  * qcom_apss_minidump_region_register() - Register a region in Minidump table.
  * @region: minidump region.
@@ -320,34 +363,58 @@  EXPORT_SYMBOL_GPL(qcom_minidump_subsystem_desc);
  */
 int qcom_apss_minidump_region_register(const struct qcom_apss_minidump_region *region)
 {
+	struct minidump_pending_region *md_pregion;
 	unsigned int num_region;
-	int ret;
-
-	if (!__md)
-		return -EPROBE_DEFER;
+	int ret = 0;
 
 	if (!qcom_apss_minidump_valid_region(region))
 		return -EINVAL;
 
 	mutex_lock(&minidump_lock);
-	ret = get_apss_minidump_region_index(region);
-	if (ret >= 0) {
-		dev_info(__md->dev, "%s region is already registered\n", region->name);
+	if (!__md) {
+		md_pregion = get_qcom_apss_pending_region(region);
+		if (md_pregion) {
+			pr_info("%s region is already registered\n", region->name);
+			ret = -EEXIST;
+			goto unlock;
+		}
+		/*
+		 * Maintain a list of client regions which came before
+		 * minidump driver was ready and once it is ready,
+		 * register them in one go from minidump probe function.
+		 */
+		md_pregion = kzalloc(sizeof(*md_pregion), GFP_KERNEL);
+		if (!md_pregion) {
+			ret = -ENOMEM;
+			goto unlock;
+		}
+		md_pregion->region = *region;
+		list_add_tail(&md_pregion->list, &md_pregion_list.pregion_list);
+		md_pregion_list.pregion_cnt++;
+		goto unlock;
+	}
+
+	if (get_apss_minidump_region_index(region) >= 0) {
+		pr_info("%s region is already registered\n", region->name);
 		ret = -EEXIST;
 		goto unlock;
 	}
 
-	/* Check if there is a room for a new entry */
+	/*
+	 * Check if there is a room for a new region.
+	 * Also, here the check for pregion_cnt is against one less
+	 * than MAX_NUM_ENTRIES as we need one entry for ELF header.
+	 */
 	num_region = le32_to_cpu(__md->md_apss_toc->region_count);
-	if (num_region >= MAX_NUM_ENTRIES) {
-		dev_err(__md->dev, "maximum region limit %u reached\n", num_region);
+	if (md_pregion_list.pregion_cnt >= (MAX_NUM_ENTRIES - 1) ||
+	    num_region >= MAX_NUM_ENTRIES) {
+		pr_err("maximum region limit %u reached\n", num_region);
 		ret = -ENOSPC;
 		goto unlock;
 	}
 
 	qcom_apss_minidump_add_region(region);
 	qcom_apss_minidump_update_elf_header(region);
-	ret = 0;
 unlock:
 	mutex_unlock(&minidump_lock);
 	return ret;
@@ -443,17 +510,23 @@  qcom_apss_minidump_clear_header(const struct qcom_apss_minidump_region *region)
  */
 int qcom_apss_minidump_region_unregister(const struct qcom_apss_minidump_region *region)
 {
+	struct minidump_pending_region *md_pregion;
 	struct minidump_region *mdr;
 	unsigned int num_region;
 	unsigned int idx;
-	int ret;
+	int ret = 0;
 
-	if (!region)
+	if (!qcom_apss_minidump_valid_region(region))
 		return -EINVAL;
 
 	mutex_lock(&minidump_lock);
 	if (!__md) {
-		ret = -EPROBE_DEFER;
+		md_pregion = get_qcom_apss_pending_region(region);
+		if (!md_pregion)
+			goto unlock;
+		list_del(&md_pregion->list);
+		kfree(md_pregion);
+		md_pregion_list.pregion_cnt--;
 		goto unlock;
 	}
 
@@ -513,6 +586,8 @@  static int qcom_minidump_init_apss_subsystem(struct minidump *md)
 
 static int qcom_minidump_probe(struct platform_device *pdev)
 {
+	struct minidump_pending_region *md_pregion;
+	struct minidump_pending_region *tmp;
 	struct minidump_global_toc *mdgtoc;
 	struct minidump *md;
 	size_t size;
@@ -551,8 +626,19 @@  static int qcom_minidump_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "Failed to add elf header: %d\n", ret);
 		memset(md->md_apss_toc, 0, sizeof(struct minidump_subsystem));
 		__md = NULL;
+		goto unlock;
 	}
 
+	list_for_each_entry_safe(md_pregion, tmp, &md_pregion_list.pregion_list, list) {
+		struct qcom_apss_minidump_region *region;
+
+		region = &md_pregion->region;
+		qcom_apss_minidump_add_region(region);
+		qcom_apss_minidump_update_elf_header(region);
+		list_del(&md_pregion->list);
+		kfree(md_pregion);
+		md_pregion_list.pregion_cnt--;
+	}
 unlock:
 	mutex_unlock(&minidump_lock);
 	return ret;