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

Commit 9a469b26 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "Minidump: Add registration/update support for suspend context"

parents ebb72ba9 4aa9d65d
Loading
Loading
Loading
Loading
+166 −48
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
 */

#include <linux/cache.h>
#include <linux/freezer.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -18,6 +19,7 @@
#include <linux/mm.h>
#include <linux/ratelimit.h>
#include <linux/sched/task.h>
#include <linux/suspend.h>
#include <linux/vmalloc.h>

#ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK
@@ -38,11 +40,20 @@ struct md_stack_cpu_data {
static int md_current_stack_init __read_mostly;

static DEFINE_PER_CPU_SHARED_ALIGNED(struct md_stack_cpu_data, md_stack_data);

struct md_suspend_context_data {
	int task_mdno;
	int stack_mdidx[STACK_NUM_PAGES];
	struct md_region stack_mdr[STACK_NUM_PAGES];
	struct md_region task_mdr;
	bool init;
};

static struct md_suspend_context_data md_suspend_context;
#endif

static bool is_vmap_stack __read_mostly;


static void __init register_log_buf(void)
{
	char **log_bufp;
@@ -64,8 +75,7 @@ static void __init register_log_buf(void)
		pr_err("Failed to add logbuf in Minidump\n");
}

static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size,
				 u32 cpu)
static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size)
{
	struct page *sp_page;
	int entry;
@@ -81,7 +91,8 @@ static int register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size,

	entry = msm_minidump_add_region(ksp_entry);
	if (entry < 0)
		pr_err("Failed to add stack of cpu %d in Minidump\n", cpu);
		pr_err("Failed to add stack of entry %s in Minidump\n",
				ksp_entry->name);
	return entry;
}

@@ -173,15 +184,14 @@ void dump_stack_minidump(u64 sp)
		for (i = 0; i < copy_pages; i++) {
			scnprintf(ksp_entry.name, sizeof(ksp_entry.name),
				  "KSTACK%d_%d", cpu, i);
			(void)register_stack_entry(&ksp_entry, sp,
						   PAGE_SIZE, cpu);
			(void)register_stack_entry(&ksp_entry, sp, PAGE_SIZE);
			sp += PAGE_SIZE;
		}
	} else {
		sp &= ~(THREAD_SIZE - 1);
		scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d",
			  cpu);
		(void)register_stack_entry(&ksp_entry, sp, THREAD_SIZE, cpu);
		(void)register_stack_entry(&ksp_entry, sp, THREAD_SIZE);
	}

	scnprintf(ktsk_entry.name, sizeof(ktsk_entry.name), "KTASK%d", cpu);
@@ -194,7 +204,7 @@ void dump_stack_minidump(u64 sp)

#ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK
static void update_stack_entry(struct md_region *ksp_entry, u64 sp,
			       int mdno, u32 cpu)
			       int mdno)
{
	struct page *sp_page;

@@ -207,72 +217,72 @@ static void update_stack_entry(struct md_region *ksp_entry, u64 sp,
	}
	if (msm_minidump_update_region(mdno, ksp_entry) < 0) {
		pr_err_ratelimited(
			"Failed to update cpu[%d] current stack in minidump\n",
			cpu);
			"Failed to update stack entry %s in minidump\n",
			ksp_entry->name);
	}
}

static void register_vmapped_stack(struct md_stack_cpu_data *md_stack_cpu_d,
				   u64 sp, u32 cpu, bool update)
static void register_vmapped_stack(struct md_region *mdr, int *mdno,
				   u64 sp, char *name_str, bool update)
{
	struct md_region *mdr;
	int *mdno;
	int i;

	sp &= ~(PAGE_SIZE - 1);
	for (i = 0; i < STACK_NUM_PAGES; i++) {
		mdr = md_stack_cpu_d->stack_mdr + i;
		mdno = md_stack_cpu_d->stack_mdidx + i;
		if (unlikely(!update)) {
			scnprintf(mdr->name, sizeof(mdr->name),
				  "KSTACK%d_%d", cpu, i);
			*mdno = register_stack_entry(mdr, sp, PAGE_SIZE, cpu);
			scnprintf(mdr->name, sizeof(mdr->name), "%s_%d",
					  name_str, i);
			*mdno = register_stack_entry(mdr, sp, PAGE_SIZE);
		} else {
			update_stack_entry(mdr, sp, *mdno, cpu);
			update_stack_entry(mdr, sp, *mdno);
		}
		sp += PAGE_SIZE;
		mdr++;
		mdno++;
	}
}

static void register_normal_stack(struct md_stack_cpu_data *md_stack_cpu_d,
				  u64 sp, u32 cpu, bool update)
static void register_normal_stack(struct md_region *mdr, int *mdno,
				  u64 sp, char *name_str, bool update)
{
	struct md_region *mdr;

	mdr = md_stack_cpu_d->stack_mdr;
	sp &= ~(THREAD_SIZE - 1);
	if (unlikely(!update)) {
		scnprintf(mdr->name, sizeof(mdr->name), "KSTACK%d", cpu);
		*md_stack_cpu_d->stack_mdidx = register_stack_entry(
						mdr, sp, THREAD_SIZE, cpu);
		scnprintf(mdr->name, sizeof(mdr->name), name_str);
		*mdno = register_stack_entry(mdr, sp, THREAD_SIZE);
	} else {
		update_stack_entry(mdr, sp,
				   *md_stack_cpu_d->stack_mdidx, cpu);
		update_stack_entry(mdr, sp, *mdno);
	}
}

static void update_md_stack(u32 cpu, u64 sp)
static void update_md_stack(struct md_region *stack_mdr,
			    int *stack_mdno, u64 sp)
{
	struct md_stack_cpu_data *md_stack_cpu_d =
				&per_cpu(md_stack_data, cpu);
	int *mdno;
	unsigned int i;

	if (is_idle_task(current) || !md_current_stack_init)
		return;
	int *mdno;

	if (likely(is_vmap_stack)) {
		for (i = 0; i < STACK_NUM_PAGES; i++) {
			mdno = md_stack_cpu_d->stack_mdidx + i;
			mdno = stack_mdno + i;
			if (unlikely(*mdno < 0))
				return;
		}
		register_vmapped_stack(md_stack_cpu_d, sp, cpu, true);
		register_vmapped_stack(stack_mdr, stack_mdno, sp, NULL, true);
	} else {
		if (unlikely(*md_stack_cpu_d->stack_mdidx < 0))
		if (unlikely(*stack_mdno < 0))
			return;
		register_normal_stack(md_stack_cpu_d, sp, cpu, true);
		register_normal_stack(stack_mdr, stack_mdno, sp, NULL, true);
	}
}

static void update_md_cpu_stack(u32 cpu, u64 sp)
{
	struct md_stack_cpu_data *md_stack_cpu_d = &per_cpu(md_stack_data, cpu);

	if (is_idle_task(current) || !md_current_stack_init)
		return;

	update_md_stack(md_stack_cpu_d->stack_mdr,
			md_stack_cpu_d->stack_mdidx, sp);
}

void md_current_stack_notifer(void *ignore, bool preempt,
@@ -281,7 +291,7 @@ void md_current_stack_notifer(void *ignore, bool preempt,
	u32 cpu = task_cpu(next);
	u64 sp = (u64)next->stack;

	update_md_stack(cpu, sp);
	update_md_cpu_stack(cpu, sp);
}

void md_current_stack_ipi_handler(void *data)
@@ -296,7 +306,45 @@ void md_current_stack_ipi_handler(void *data)
		stack_vm_area = task_stack_vm_area(current);
		sp = (u64)stack_vm_area->addr;
	}
	update_md_stack(cpu, sp);
	update_md_cpu_stack(cpu, sp);
}

static void update_md_current_task(struct md_region *mdr, int mdno)
{
	mdr->virt_addr = (u64)current;
	mdr->phys_addr = virt_to_phys((uintptr_t *)current);
	if (msm_minidump_update_region(mdno, mdr) < 0)
		pr_err("Failed to update %s current task in minidump\n",
			   mdr->name);
}

static void update_md_suspend_current_stack(void)
{
	u64 sp = current_stack_pointer;
	struct vm_struct *stack_vm_area;

	if (likely(is_vmap_stack)) {
		stack_vm_area = task_stack_vm_area(current);
		sp = (u64)stack_vm_area->addr;
	}
	update_md_stack(md_suspend_context.stack_mdr,
			md_suspend_context.stack_mdidx, sp);
}

static void update_md_suspend_current_task(void)
{
	if (unlikely(md_suspend_context.task_mdno < 0))
		return;
	update_md_current_task(&md_suspend_context.task_mdr,
			md_suspend_context.task_mdno);
}

static void update_md_suspend_currents(void)
{
	if (!md_suspend_context.init)
		return;
	update_md_suspend_current_stack();
	update_md_suspend_current_task();
}

static void register_current_stack(void)
@@ -305,6 +353,7 @@ static void register_current_stack(void)
	u64 sp = current_stack_pointer;
	struct md_stack_cpu_data *md_stack_cpu_d;
	struct vm_struct *stack_vm_area;
	char name_str[MAX_NAME_LENGTH];

	/*
	 * Since stacks are now allocated with vmalloc, the translation to
@@ -324,16 +373,85 @@ static void register_current_stack(void)
		 * once system up and running, let the cpu update its currents.
		 */
		md_stack_cpu_d = &per_cpu(md_stack_data, cpu);
		scnprintf(name_str, sizeof(name_str), "KSTACK%d", cpu);
		if (is_vmap_stack)
			register_vmapped_stack(md_stack_cpu_d, sp, cpu, false);
			register_vmapped_stack(md_stack_cpu_d->stack_mdr,
				md_stack_cpu_d->stack_mdidx, sp,
				name_str, false);
		else
			register_normal_stack(md_stack_cpu_d, sp, cpu, false);
			register_normal_stack(md_stack_cpu_d->stack_mdr,
				md_stack_cpu_d->stack_mdidx, sp,
				name_str, false);
	}

	register_trace_sched_switch(md_current_stack_notifer, NULL);
	md_current_stack_init = 1;
	smp_call_function(md_current_stack_ipi_handler, NULL, 1);
}

static void register_suspend_stack(void)
{
	char name_str[MAX_NAME_LENGTH];
	u64 sp = current_stack_pointer;
	struct vm_struct *stack_vm_area = task_stack_vm_area(current);

	scnprintf(name_str, sizeof(name_str), "KSUSPSTK");
	if (is_vmap_stack) {
		sp = (u64)stack_vm_area->addr;
		register_vmapped_stack(md_suspend_context.stack_mdr,
				md_suspend_context.stack_mdidx,
				sp, name_str, false);
	} else {
		register_normal_stack(md_suspend_context.stack_mdr,
			md_suspend_context.stack_mdidx,
			sp, name_str, false);
	}
}

static void register_current_task(struct md_region *mdr, int *mdno,
				  char *name_str)
{
	scnprintf(mdr->name, sizeof(mdr->name), name_str);
	mdr->virt_addr = (u64)current;
	mdr->phys_addr = virt_to_phys((uintptr_t *)current);
	mdr->size = sizeof(struct task_struct);
	*mdno = msm_minidump_add_region(mdr);
	if (*mdno < 0)
		pr_err("Failed to add current task %s in Minidump\n",
		       mdr->name);
}

static void register_suspend_current_task(void)
{
	char name_str[MAX_NAME_LENGTH];

	scnprintf(name_str, sizeof(name_str), "KSUSPTASK");
	register_current_task(&md_suspend_context.task_mdr,
			&md_suspend_context.task_mdno, name_str);
}

static int minidump_pm_notifier(struct notifier_block *nb,
				unsigned long event, void *unused)
{
	switch (event) {
	case PM_SUSPEND_PREPARE:
		update_md_suspend_currents();
		break;
	}
	return NOTIFY_DONE;
}

static struct notifier_block minidump_pm_nb = {
	.notifier_call = minidump_pm_notifier,
};

static void register_suspend_context(void)
{
	register_suspend_stack();
	register_suspend_current_task();
	register_pm_notifier(&minidump_pm_nb);
	md_suspend_context.init = true;
}
#endif

#ifdef CONFIG_ARM64
@@ -356,15 +474,14 @@ static void register_irq_stack(void)
					  sizeof(irq_sp_entry.name),
					  "KISTACK%d_%d", cpu, i);
				register_stack_entry(&irq_sp_entry, sp,
						     PAGE_SIZE, cpu);
						     PAGE_SIZE);
				sp += PAGE_SIZE;
			}
		} else {
			sp = irq_stack_base;
			scnprintf(irq_sp_entry.name, sizeof(irq_sp_entry.name),
				  "KISTACK%d", cpu);
			register_stack_entry(&irq_sp_entry, sp, IRQ_STACK_SIZE,
					     cpu);
			register_stack_entry(&irq_sp_entry, sp, IRQ_STACK_SIZE);
		}
	}
}
@@ -379,6 +496,7 @@ static int __init msm_minidump_log_init(void)
	register_irq_stack();
#ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK
	register_current_stack();
	register_suspend_context();
#endif
	register_log_buf();
	return 0;
+8 −0
Original line number Diff line number Diff line
@@ -105,6 +105,13 @@ static int qcom_soc_wdt_probe(struct platform_device *pdev)
	return qcom_wdt_register(pdev, wdog_dd, "msm-watchdog");
}

static const struct dev_pm_ops qcom_soc_dev_pm_ops = {
#ifdef CONFIG_PM_SLEEP
	.suspend_late = qcom_wdt_pet_suspend,
	.resume_early = qcom_wdt_pet_resume,
#endif
};

static const struct of_device_id qcom_soc_match_table[] = {
	{ .compatible = "qcom,msm-watchdog" },
	{}
@@ -115,6 +122,7 @@ static struct platform_driver qcom_soc_wdt_driver = {
	.remove = qcom_wdt_remove,
	.driver = {
		.name = "msm_watchdog",
		.pm = &qcom_soc_dev_pm_ops,
		.of_match_table = qcom_soc_match_table,
	},
};
+54 −5
Original line number Diff line number Diff line
@@ -89,6 +89,45 @@ static void qcom_wdt_resume(void)
	wdog_data->last_pet = sched_clock();
	return;
}

int qcom_wdt_pet_suspend(struct device *dev)
{
	struct msm_watchdog_data *wdog_dd =
			(struct msm_watchdog_data *)dev_get_drvdata(dev);

	if (!wdog_dd)
		return 0;

	wdog_dd->ops->reset_wdt(wdog_dd);
	wdog_dd->last_pet = sched_clock();
	spin_lock(&wdog_dd->freeze_lock);
	wdog_dd->freeze_in_progress = true;
	spin_unlock(&wdog_dd->freeze_lock);
	del_timer_sync(&wdog_dd->pet_timer);
	return 0;
}
EXPORT_SYMBOL(qcom_wdt_pet_suspend);

int qcom_wdt_pet_resume(struct device *dev)
{
	struct msm_watchdog_data *wdog_dd =
			(struct msm_watchdog_data *)dev_get_drvdata(dev);
	unsigned long delay_time = 0;

	if (!wdog_dd)
		return 0;

	delay_time = msecs_to_jiffies(wdog_dd->pet_time);
	wdog_dd->ops->reset_wdt(wdog_dd);
	wdog_dd->last_pet = sched_clock();
	spin_lock(&wdog_dd->freeze_lock);
	wdog_dd->pet_timer.expires = jiffies + delay_time;
	add_timer(&wdog_dd->pet_timer);
	wdog_dd->freeze_in_progress = false;
	spin_unlock(&wdog_dd->freeze_lock);
	return 0;
}
EXPORT_SYMBOL(qcom_wdt_pet_resume);
#endif

static struct syscore_ops qcom_wdt_syscore_ops = {
@@ -339,8 +378,13 @@ static __ref int qcom_wdt_kthread(void *arg)
		/* Check again before scheduling
		 * Could have been changed on other cpu
		 */
		if (!kthread_should_stop())
			mod_timer(&wdog_dd->pet_timer, jiffies + delay_time);
		if (!kthread_should_stop()) {
			spin_lock(&wdog_dd->freeze_lock);
			if (!wdog_dd->freeze_in_progress)
				mod_timer(&wdog_dd->pet_timer,
					  jiffies + delay_time);
			spin_unlock(&wdog_dd->freeze_lock);
		}
	}
	return 0;
}
@@ -444,6 +488,9 @@ static irqreturn_t qcom_wdt_bark_handler(int irq, void *dev_id)
	if (wdog_dd->do_ipi_ping)
		qcom_wdt_dump_cpu_alive_mask(wdog_dd);

	if (wdog_dd->freeze_in_progress)
		dev_info(wdog_dd->dev, "Suspend in progress\n");

	qcom_wdt_trigger_bite();

	return IRQ_HANDLED;
@@ -474,7 +521,7 @@ static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd,

	ret = devm_request_irq(wdog_dd->dev, wdog_dd->bark_irq,
			       qcom_wdt_bark_handler,
					IRQF_TRIGGER_RISING,
			       IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
			       "apps_wdog_bark", wdog_dd);
	if (ret) {
		dev_err(wdog_dd->dev, "failed to request bark irq\n");
@@ -495,6 +542,8 @@ static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd,
	wdog_dd->timer_expired = false;
	wdog_dd->user_pet_complete = true;
	wdog_dd->user_pet_enabled = false;
	spin_lock_init(&wdog_dd->freeze_lock);
	wdog_dd->freeze_in_progress = false;
	wake_up_process(wdog_dd->watchdog_task);
	timer_setup(&wdog_dd->pet_timer, qcom_wdt_pet_task_wakeup, 0);
	wdog_dd->pet_timer.expires = jiffies + delay_time;
+8 −0
Original line number Diff line number Diff line
@@ -261,6 +261,13 @@ static int hh_wdt_probe(struct platform_device *pdev)
	return qcom_wdt_register(pdev, wdog_dd, "hh-watchdog");
}

static const struct dev_pm_ops hh_wdt_dev_pm_ops = {
#ifdef CONFIG_PM_SLEEP
	.suspend_late = qcom_wdt_pet_suspend,
	.resume_early = qcom_wdt_pet_resume,
#endif
};

static const struct of_device_id hh_wdt_match_table[] = {
	{ .compatible = "qcom,hh-watchdog" },
	{}
@@ -271,6 +278,7 @@ static struct platform_driver hh_wdt_driver = {
	.remove = qcom_wdt_remove,
	.driver = {
		.name = "hh-watchdog",
		.pm = &hh_wdt_dev_pm_ops,
		.of_match_table = hh_wdt_match_table,
	},
};
+4 −0
Original line number Diff line number Diff line
@@ -123,12 +123,16 @@ struct msm_watchdog_data {
	unsigned long long ping_start[NR_CPUS];
	unsigned long long ping_end[NR_CPUS];
	int cpu_idle_pc_state[NR_CPUS];
	bool freeze_in_progress;
	spinlock_t freeze_lock;
};

extern void qcom_wdt_trigger_bite(void);
int qcom_wdt_register(struct platform_device *pdev,
			struct msm_watchdog_data *wdog_dd,
			char *wdog_dd_name);
int qcom_wdt_pet_suspend(struct device *dev);
int qcom_wdt_pet_resume(struct device *dev);
int qcom_wdt_remove(struct platform_device *pdev);
#else
static inline void qcom_wdt_trigger_bite(void) { }