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

Commit b94e7fae authored by Steven Cahail's avatar Steven Cahail Committed by Matt Wagantall
Browse files

soc: qcom: glink_ssr: Handle multiple link-up notifications correctly



The glink_ssr driver currently attempts to open a new glink_ssr channel for
every link_up notification it receives. Since each subsystem information
structure only stores one channel handle, this can cause communication
failures when more than one link_up notification is received for the same
edge.

Once an edge's glink_ssr channel is opened successfully, unregister from
receiving link-up notifications on that edge. When the channel is closed,
re-register for notifications on that edge. This ensures that only one
glink_ssr channel is opened for each link-up notification.

Change-Id: I45dd649e1b003e0398e07b478eece10d0d5271b8
Signed-off-by: default avatarSteven Cahail <scahail@codeaurora.org>
parent 088d0917
Loading
Loading
Loading
Loading
+18 −8
Original line number Diff line number Diff line
@@ -636,25 +636,32 @@ enum ssr_command {
 * edge:		name of the G-Link edge
 * xprt:		name of the G-Link transport
 * handle:		glink_ssr channel used for this subsystem
 * link_state_handle:	link state handle for this edge, used to unregister
 *			from receiving link state callbacks
 * link_info:		Transport info used in link state callback registration
 * cb_data:	Private callback data structure for notification functions
 * cb_data:		Private callback data structure for notification
 *			functions
 * subsystem_list_node:	used to chain this structure in a list of subsystem
 *			info structures
 * notify_list:	list of subsys_info_leaf structures, containing the subsystems
 *		to notify if this subsystem undergoes SSR
 * notify_list:		list of subsys_info_leaf structures, containing the
 *			subsystems to notify if this subsystem undergoes SSR
 * notify_list_len:	length of notify_list
 * link_up:		Flag indicating whether transport is up or not
 * link_up_lock:	Lock for protecting the link_up flag
 */
struct subsys_info {
	const char *ssr_name;
	const char *edge;
	const char *xprt;
	void *handle;
	void *link_state_handle;
	struct glink_link_info *link_info;
	struct ssr_notify_data *cb_data;
	struct list_head subsystem_list_node;
	struct list_head notify_list;
	int notify_list_len;
	bool link_up;
	spinlock_t link_up_lock;
};

/**
@@ -713,6 +720,8 @@ struct cleanup_done_msg {
 * responded:	Indicates whether or not a cleanup_done response was received.
 * version:	G-Link SSR protocol version
 * seq_num:	G-Link SSR protocol sequence number
 * edge:	The G-Link edge name for the channel associated with this
 *		callback data
 */
struct ssr_notify_data {
	bool tx_done;
@@ -720,6 +729,7 @@ struct ssr_notify_data {
	bool responded;
	uint32_t version;
	uint32_t seq_num;
	const char *edge;
};

/**
+56 −6
Original line number Diff line number Diff line
@@ -67,10 +67,12 @@ struct configure_and_open_ch_work {

/**
 * struct close_ch_work - Work structure for used for closing glink_ssr channels
 * edge:	The G-Link edge name for the channel being closed
 * handle:	G-Link channel handle to be closed
 * work:	Work structure
 */
struct close_ch_work {
	char edge[GLINK_NAME_SIZE];
	void *handle;
	struct work_struct work;
};
@@ -88,6 +90,7 @@ static wait_queue_head_t waitqueue;

static void link_state_cb_worker(struct work_struct *work)
{
	unsigned long flags;
	struct configure_and_open_ch_work *ch_open_work =
		container_of(work, struct configure_and_open_ch_work, work);
	struct subsys_info *ss_info = ch_open_work->ss_info;
@@ -97,11 +100,24 @@ static void link_state_cb_worker(struct work_struct *work)
			ch_open_work->transport);

	if (ss_info && ch_open_work->link_state == GLINK_LINK_STATE_UP) {
		configure_and_open_channel(ss_info);
		spin_lock_irqsave(&ss_info->link_up_lock, flags);
		if (!ss_info->link_up) {
			ss_info->link_up = true;
			spin_unlock_irqrestore(&ss_info->link_up_lock, flags);
			if (!configure_and_open_channel(ss_info)) {
				glink_unregister_link_state_cb(
						ss_info->link_state_handle);
				ss_info->link_state_handle = NULL;
			}
			kfree(ch_open_work);
			return;
		}
		spin_unlock_irqrestore(&ss_info->link_up_lock, flags);
	} else {
		if (ss_info) {
			spin_lock_irqsave(&ss_info->link_up_lock, flags);
			ss_info->link_up = false;
			spin_unlock_irqrestore(&ss_info->link_up_lock, flags);
			ss_info->handle = NULL;
		} else {
			GLINK_ERR("<SSR> %s: ss_info is NULL\n", __func__);
@@ -238,9 +254,30 @@ void glink_ssr_notify_tx_done(void *handle, const void *priv,

void close_ch_worker(struct work_struct *work)
{
	unsigned long flags;
	void *link_state_handle;
	struct subsys_info *ss_info;
	struct close_ch_work *close_work =
		container_of(work, struct close_ch_work, work);

	glink_close(close_work->handle);

	ss_info = get_info_for_edge(close_work->edge);
	spin_lock_irqsave(&ss_info->link_up_lock, flags);
	ss_info->link_up = false;
	spin_unlock_irqrestore(&ss_info->link_up_lock, flags);

	BUG_ON(ss_info->link_state_handle != NULL);
	link_state_handle = glink_register_link_state_cb(ss_info->link_info,
			NULL);

	if (IS_ERR_OR_NULL(link_state_handle))
		GLINK_ERR("<SSR> %s: %s, ret[%zu]\n", __func__,
				"Couldn't register link state cb",
				PTR_ERR(link_state_handle));
	else
		ss_info->link_state_handle = link_state_handle;

	kfree(close_work);
}

@@ -275,6 +312,8 @@ void glink_ssr_notify_state(void *handle, const void *priv, unsigned event)
				return;
			}

			strlcpy(close_work->edge, cb_data->edge,
					sizeof(close_work->edge));
			close_work->handle = handle;
			INIT_WORK(&close_work->work, close_ch_worker);
			queue_work(glink_ssr_wq, &close_work->work);
@@ -372,6 +411,7 @@ int notify_for_subsystem(struct subsys_info *ss_info)
	void *handle;
	int wait_ret;
	int ret;
	unsigned long flags;

	if (!ss_info) {
		GLINK_ERR("<SSR> %s: ss_info structure invalid\n", __func__);
@@ -407,6 +447,7 @@ int notify_for_subsystem(struct subsys_info *ss_info)
		handle = ss_info_channel->handle;
		ss_leaf_entry->cb_data = ss_info_channel->cb_data;

		spin_lock_irqsave(&ss_info->link_up_lock, flags);
		if (IS_ERR_OR_NULL(ss_info_channel->handle) ||
				!ss_info_channel->cb_data ||
				!ss_info_channel->link_up ||
@@ -420,9 +461,11 @@ int notify_for_subsystem(struct subsys_info *ss_info)
				ss_info_channel->handle, "link_up",
				ss_info_channel->link_up);

			spin_unlock_irqrestore(&ss_info->link_up_lock, flags);
			atomic_dec(&responses_remaining);
			continue;
		}
		spin_unlock_irqrestore(&ss_info->link_up_lock, flags);

		do_cleanup_data = kmalloc(sizeof(struct do_cleanup_msg),
				GFP_KERNEL);
@@ -528,14 +571,17 @@ static int configure_and_open_channel(struct subsys_info *ss_info)
	}
	cb_data->responded = false;
	cb_data->event = GLINK_SSR_EVENT_INIT;
	cb_data->edge = ss_info->edge;
	ss_info->cb_data = cb_data;

	memset(&open_cfg, 0, sizeof(struct glink_open_config));

	if (ss_info->xprt)
	if (ss_info->xprt) {
		open_cfg.transport = ss_info->xprt;
	else
	} else {
		open_cfg.transport = NULL;
		open_cfg.options = GLINK_OPT_INITIAL_XPORT;
	}
	open_cfg.edge = ss_info->edge;
	open_cfg.name = "glink_ssr";
	open_cfg.notify_rx = glink_ssr_notify_rx;
@@ -546,8 +592,9 @@ static int configure_and_open_channel(struct subsys_info *ss_info)

	handle = glink_open(&open_cfg);
	if (IS_ERR_OR_NULL(handle)) {
		GLINK_ERR("<SSR> %s:%s %s: unable to open channel\n",
				 open_cfg.edge, open_cfg.name, __func__);
		GLINK_ERR("<SSR> %s:%s %s: unable to open channel, ret[%zu]\n",
				 open_cfg.edge, open_cfg.name, __func__,
				 PTR_ERR(handle));
		kfree(cb_data);
		cb_data = NULL;
		ss_info->cb_data = NULL;
@@ -735,7 +782,9 @@ static int glink_ssr_probe(struct platform_device *pdev)
	ss_info->link_info->glink_link_state_notif_cb = glink_ssr_link_state_cb;
	ss_info->link_up = false;
	ss_info->handle = NULL;
	ss_info->link_state_handle = NULL;
	ss_info->cb_data = NULL;
	spin_lock_init(&ss_info->link_up_lock);

	nb = kmalloc(sizeof(struct restart_notifier_block), GFP_KERNEL);
	if (!nb) {
@@ -811,6 +860,7 @@ static int glink_ssr_probe(struct platform_device *pdev)
		ret = PTR_ERR(link_state_handle);
		goto link_state_register_fail;
	}
	ss_info->link_state_handle = link_state_handle;

	return 0;