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

Commit b29669b7 authored by Sagar Dharia's avatar Sagar Dharia
Browse files

slim_ngd: Fix logical address notification mechanism



It's possible that multiple slaves report present/absent and expect
corresponding notifications concurrently. Avoid using controller
resources where possible, and use list_for_each_safe to allow
changes to the device-list during list traversal.

Change-Id: I371c48d25a0953f5f1909c904e8fd80b599d16b3
Signed-off-by: default avatarSagar Dharia <sdharia@codeaurora.org>
parent 02983880
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -318,7 +318,7 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
				return -EREMOTEIO;
			timeout = wait_for_completion_timeout(&dev->ctrl_up,
							HZ);
			if (!timeout)
			if (!timeout && dev->state == MSM_CTRL_DOWN)
				return -ETIMEDOUT;
		}
		ret = msm_slim_get_ctrl(dev);
@@ -1046,11 +1046,13 @@ static void ngd_laddr_lookup(struct work_struct *work)
		container_of(work, struct msm_slim_ctrl, slave_notify);
	struct slim_controller *ctrl = &dev->ctrl;
	struct slim_device *sbdev;
	struct list_head *pos, *next;
	int i;
	slim_framer_booted(ctrl);
	mutex_lock(&ctrl->m_ctrl);
	list_for_each_entry(sbdev, &ctrl->devs, dev_list) {
	list_for_each_safe(pos, next, &ctrl->devs) {
		int ret = 0;
		sbdev = list_entry(pos, struct slim_device, dev_list);
		mutex_unlock(&ctrl->m_ctrl);
		for (i = 0; i < LADDR_RETRY; i++) {
			ret = slim_get_logical_addr(sbdev, sbdev->e_addr,
+18 −23
Original line number Diff line number Diff line
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -288,36 +288,26 @@ static struct device_type slim_dev_type = {

static void slim_report(struct work_struct *work)
{
	u8 laddr;
	int ret, i;
	struct slim_driver *sbdrv;
	struct slim_device *sbdev =
			container_of(work, struct slim_device, wd);
	struct slim_controller *ctrl = sbdev->ctrl;
	if (!sbdev->dev.driver)
		return;
	/* check if device-up or down needs to be called */
	mutex_lock(&ctrl->m_ctrl);
	/* address no longer valid, means device reported absent */
	for (i = 0; i < ctrl->num_dev; i++) {
		if (sbdev->laddr == ctrl->addrt[i].laddr &&
			ctrl->addrt[i].valid == false &&
			sbdev->notified)
			break;
	}
	mutex_unlock(&ctrl->m_ctrl);
	if ((!sbdev->reported && !sbdev->notified) ||
			(sbdev->reported && sbdev->notified))
		return;

	sbdrv = to_slim_driver(sbdev->dev.driver);
	if (i < ctrl->num_dev) {
	/*
	 * address no longer valid, means device reported absent, whereas
	 * address valid, means device reported present
	 */
	if (sbdev->notified && !sbdev->reported) {
		sbdev->notified = false;
		if (sbdrv->device_down)
			sbdrv->device_down(sbdev);
		return;
	}
	if (sbdev->notified)
		return;
	ret = slim_get_logical_addr(sbdev, sbdev->e_addr, 6, &laddr);
	if (!ret) {
		if (sbdrv)
	} else if (!sbdev->notified && sbdev->reported) {
		sbdev->notified = true;
		if (sbdrv->device_up)
			sbdrv->device_up(sbdev);
@@ -633,6 +623,7 @@ void slim_report_absent(struct slim_device *sbdev)
			ctrl->addrt[i].valid = false;
	}
	mutex_unlock(&ctrl->m_ctrl);
	sbdev->reported = false;
	queue_work(ctrl->wq, &sbdev->wd);
}
EXPORT_SYMBOL(slim_report_absent);
@@ -791,6 +782,8 @@ int slim_assign_laddr(struct slim_controller *ctrl, const u8 *e_addr,
	u8 i = 0;
	bool exists = false;
	struct slim_device *sbdev;
	struct list_head *pos, *next;

	mutex_lock(&ctrl->m_ctrl);
	/* already assigned */
	if (ctrl_getlogical_addr(ctrl, e_addr, e_len, &i) == 0) {
@@ -840,10 +833,12 @@ ret_assigned_laddr:
	pr_info("slimbus:%d laddr:0x%x, EAPC:0x%x:0x%x", ctrl->nr, *laddr,
				e_addr[1], e_addr[2]);
	mutex_lock(&ctrl->m_ctrl);
	list_for_each_entry(sbdev, &ctrl->devs, dev_list) {
	list_for_each_safe(pos, next, &ctrl->devs) {
		sbdev = list_entry(pos, struct slim_device, dev_list);
		if (memcmp(sbdev->e_addr, e_addr, 6) == 0) {
			struct slim_driver *sbdrv;
			sbdev->laddr = *laddr;
			sbdev->reported = true;
			if (sbdev->dev.driver) {
				sbdrv = to_slim_driver(sbdev->dev.driver);
				if (sbdrv->device_up)
+6 −1
Original line number Diff line number Diff line
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -634,6 +634,10 @@ struct slim_pending_ch {
 *  @driver: Device's driver. Pointer to access routines.
 *  @ctrl: Slimbus controller managing the bus hosting this device.
 *  @laddr: 1-byte Logical address of this device.
 *  @reported: Flag to indicate whether this device reported present. The flag
 *	is set when device reports present, and is reset when it reports
 *	absent. This flag alongwith notified flag below is used to call
 *	device_up, or device_down callbacks for driver of this device.
 *  @mark_define: List of channels pending definition/activation.
 *  @mark_suspend: List of channels pending suspend.
 *  @mark_removal: List of channels pending removal.
@@ -657,6 +661,7 @@ struct slim_device {
	struct slim_driver	*driver;
	struct slim_controller	*ctrl;
	u8			laddr;
	bool			reported;
	struct list_head	mark_define;
	struct list_head	mark_suspend;
	struct list_head	mark_removal;