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

Commit d561d4ae authored by Isaac J. Manjarres's avatar Isaac J. Manjarres
Browse files

soc: qcom: subsys_notif: Add support for early SSR notifications



Currently, the SSR framework sends subsystem state transtition
notifications during SSR to inform interested clients about
the subsystem's state. However, there may be clients of the
notification framework that would like to know that a
subsystem has gone down, and before that subsystem has started
undergoing SSR, so that they can ensure that communication
towards a downed subsystem ceases.

Add support for registering callbacks to be invoked as soon as
a subsystem crashes, and before commencing SSR.

Change-Id: Ia3ce647a7b1167b44ab9c97b4b0b0637e204b116
Signed-off-by: default avatarIsaac J. Manjarres <isaacm@codeaurora.org>
parent 71f9600f
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
@@ -21,10 +21,25 @@
#include <linux/slab.h>
#include <soc/qcom/subsystem_notif.h>

/**
 * The callbacks that are registered in this data structure as early
 * notification callbacks will be called as soon as the SSR framework is
 * informed that the subsystem has crashed. This means that these functions will
 * be invoked as part of an IRQ handler, and thus, will be called in an atomic
 * context. Therefore, functions that are registered as early notification
 * callback must obey to the same constraints as interrupt handlers
 * (i.e. these functions must not sleep or block, etc).
 */
struct subsys_early_notif_info {
	spinlock_t cb_lock;
	void (*early_notif_cb[NUM_EARLY_NOTIFS])(void *);
	void *data[NUM_EARLY_NOTIFS];
};

struct subsys_notif_info {
	char name[50];
	struct srcu_notifier_head subsys_notif_rcvr_list;
	struct subsys_early_notif_info early_notif_info;
	struct list_head list;
};

@@ -94,6 +109,105 @@ int subsys_notif_unregister_notifier(void *subsys_handle,
}
EXPORT_SYMBOL(subsys_notif_unregister_notifier);

void send_early_notifications(void *early_notif_handle)
{
	struct subsys_early_notif_info *early_info = early_notif_handle;
	unsigned long flags;
	unsigned int i;
	void (*notif_cb)(void *data);

	if (!early_notif_handle)
		return;

	spin_lock_irqsave(&early_info->cb_lock, flags);
	for (i = 0; i < NUM_EARLY_NOTIFS; i++) {
		notif_cb = early_info->early_notif_cb[i];
		if (notif_cb)
			notif_cb(early_info->data[i]);
	}
	spin_unlock_irqrestore(&early_info->cb_lock, flags);
}
EXPORT_SYMBOL(send_early_notifications);

static bool valid_early_notif(enum early_subsys_notif_type notif_type)
{
	return  notif_type >= 0 && notif_type < NUM_EARLY_NOTIFS;
}

/**
 * The early_notif_cb parameter must point to a function that conforms to the
 * same constraints placed upon interrupt handlers, as the function will be
 * called in an atomic context (i.e. these functions must not sleep or block).
 */
int subsys_register_early_notifier(const char *subsys_name,
				   enum early_subsys_notif_type notif_type,
				   void (*early_notif_cb)(void *), void *data)
{
	struct subsys_notif_info *subsys;
	struct subsys_early_notif_info *early_notif_info;
	unsigned long flags;
	int rc = 0;

	if (!subsys_name || !early_notif_cb || !valid_early_notif(notif_type))
		return -EINVAL;

	subsys = _notif_find_subsys(subsys_name);
	if (!subsys)
		return -EINVAL;

	early_notif_info = &subsys->early_notif_info;
	spin_lock_irqsave(&early_notif_info->cb_lock, flags);
	if (early_notif_info->early_notif_cb[notif_type]) {
		rc = -EEXIST;
		goto out;
	}
	early_notif_info->early_notif_cb[notif_type] = early_notif_cb;
	early_notif_info->data[notif_type] = data;
out:
	spin_unlock_irqrestore(&early_notif_info->cb_lock, flags);
	return rc;
}
EXPORT_SYMBOL(subsys_register_early_notifier);

int subsys_unregister_early_notifier(const char *subsys_name, enum
				     early_subsys_notif_type notif_type)
{
	struct subsys_notif_info *subsys;
	struct subsys_early_notif_info *early_notif_info;
	unsigned long flags;

	if (!subsys_name || !valid_early_notif(notif_type))
		return -EINVAL;

	subsys = _notif_find_subsys(subsys_name);
	if (!subsys)
		return -EINVAL;

	early_notif_info = &subsys->early_notif_info;
	spin_lock_irqsave(&early_notif_info->cb_lock, flags);
	early_notif_info->early_notif_cb[notif_type] = NULL;
	early_notif_info->data[notif_type] = NULL;
	spin_unlock_irqrestore(&early_notif_info->cb_lock, flags);
	return 0;
}
EXPORT_SYMBOL(subsys_unregister_early_notifier);

void *subsys_get_early_notif_info(const char *subsys_name)
{
	struct subsys_notif_info *subsys;

	if (!subsys_name)
		return ERR_PTR(-EINVAL);

	subsys = _notif_find_subsys(subsys_name);

	if (!subsys)
		return ERR_PTR(-EINVAL);

	return &subsys->early_notif_info;
}
EXPORT_SYMBOL(subsys_get_early_notif_info);

void *subsys_notif_add_subsys(const char *subsys_name)
{
	struct subsys_notif_info *subsys = NULL;
@@ -121,6 +235,9 @@ void *subsys_notif_add_subsys(const char *subsys_name)

	srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list);

	memset(&subsys->early_notif_info, 0, sizeof(struct
						    subsys_early_notif_info));
	spin_lock_init(&subsys->early_notif_info.cb_lock);
	INIT_LIST_HEAD(&subsys->list);

	mutex_lock(&notif_lock);
+4 −0
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ struct subsys_device {
	struct subsys_tracking track;

	void *notify;
	void *early_notify;
	struct device dev;
	struct module *owner;
	int count;
@@ -1218,6 +1219,8 @@ int subsystem_restart_dev(struct subsys_device *dev)

	name = dev->desc->name;

	send_early_notifications(dev->early_notify);

	/*
	 * If a system reboot/shutdown is underway, ignore subsystem errors.
	 * However, print a message so that we know that a subsystem behaved
@@ -1792,6 +1795,7 @@ struct subsys_device *subsys_register(struct subsys_desc *desc)
			sizeof(subsys->desc->fw_name));

	subsys->notify = subsys_notif_add_subsys(desc->name);
	subsys->early_notify = subsys_get_early_notif_info(desc->name);

	snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
	wakeup_source_init(&subsys->ssr_wlock, subsys->wlname);
+37 −0
Original line number Diff line number Diff line
@@ -25,6 +25,11 @@ enum subsys_notif_type {
	SUBSYS_NOTIF_TYPE_COUNT
};

enum early_subsys_notif_type {
	XPORT_LAYER_NOTIF,
	NUM_EARLY_NOTIFS
};

#if defined(CONFIG_MSM_SUBSYSTEM_RESTART)
/* Use the subsys_notif_register_notifier API to register for notifications for
 * a particular subsystem. This API will return a handle that can be used to
@@ -50,6 +55,14 @@ void *subsys_notif_add_subsys(const char *subsys_name);
int subsys_notif_queue_notification(void *subsys_handle,
					enum subsys_notif_type notif_type,
					void *data);
void *subsys_get_early_notif_info(const char *subsys_name);
int subsys_register_early_notifier(const char *subsys_name,
				   enum early_subsys_notif_type notif_type,
				   void (*early_notif_cb)(void *),
				   void *data);
int subsys_unregister_early_notifier(const char *subsys_name, enum
				     early_subsys_notif_type notif_type);
void send_early_notifications(void *early_notif_handle);
#else

static inline void *subsys_notif_register_notifier(
@@ -75,6 +88,30 @@ static inline int subsys_notif_queue_notification(void *subsys_handle,
{
	return 0;
}

static inline void *subsys_get_early_notif_info(const char *subsys_name)
{
	return NULL;
}

static inline int subsys_register_early_notifier(const char *subsys_name,
				   enum early_subsys_notif_type notif_type,
				   void (*early_notif_cb)(void *),
				   void *data)
{
	return -ENOTSUPP;
}

static inline int subsys_unregister_early_notifier(const char *subsys_name,
						   enum early_subsys_notif_type
						   notif_type)
{
	return -ENOTSUPP;
}

static inline void send_early_notifications(void *early_notif_handle)
{
}
#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */

#endif