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

Commit 81db557a authored by Jinesh K. Jayakumar's avatar Jinesh K. Jayakumar Committed by Gerrit - the friendly Code Review server
Browse files

msm: ipa: eth: Use single netdevice notifier registration



Registering with netdevice notifier while holding upper_mutex can
deadlock if an active notification is in parallel waiting for the
same mutex, as the rtnl mutex needed for the registation is held
by the notification handler in this scenario. Re-use the notifier
registration used for real device to also monitor for events from
upper interfaces.

Change-Id: Ia2ef72be4b831077050e18c93781ea620e956820
Signed-off-by: default avatarJinesh K. Jayakumar <jineshk@codeaurora.org>
parent e26c537a
Loading
Loading
Loading
Loading
+8 −5
Original line number Diff line number Diff line
@@ -306,9 +306,11 @@ static void ipa_eth_device_refresh(struct ipa_eth_device *eth_dev)
			return;
		}

		if (ipa_eth_net_watch_upper(eth_dev))
		if (ipa_eth_net_register_upper(eth_dev)) {
			ipa_eth_dev_err(eth_dev,
					"Failed to watch upper interfaces");
				"Failed to register upper interfaces");
			eth_dev->of_state = IPA_ETH_OF_ST_ERROR;
		}

		if (ipa_eth_pm_vote_bw(eth_dev))
			ipa_eth_dev_err(eth_dev,
@@ -316,9 +318,11 @@ static void ipa_eth_device_refresh(struct ipa_eth_device *eth_dev)
	} else {
		ipa_eth_dev_log(eth_dev, "Start is disallowed for the device");

		if (ipa_eth_net_unwatch_upper(eth_dev))
		if (ipa_eth_net_unregister_upper(eth_dev)) {
			ipa_eth_dev_err(eth_dev,
					"Failed to unwatch upper interfaces");
				"Failed to unregister upper interfaces");
			eth_dev->of_state = IPA_ETH_OF_ST_ERROR;
		}

		if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) {
			IPA_ACTIVE_CLIENTS_INC_SIMPLE();
@@ -601,7 +605,6 @@ struct ipa_eth_device *ipa_eth_alloc_device(

	ipa_priv->eth_dev = eth_dev;

	mutex_init(&ipa_priv->upper_mutex);
	INIT_LIST_HEAD(&ipa_priv->upper_devices);

	ipa_priv->panic_nb.notifier_call = ipa_eth_panic_notifier;
+6 −14
Original line number Diff line number Diff line
@@ -476,15 +476,11 @@ int ipa_eth_ep_register_upper_interface(
	struct net_device *net_dev = upper_eth_dev->net_dev;
	struct ipa_eth_device *eth_dev = upper_eth_dev->eth_dev;

	ipa_eth_dev_log(eth_dev,
		"Registering upper interface %s", net_dev->name);
	if (upper_eth_dev->registered)
		return 0;

	if (upper_eth_dev->registered) {
	ipa_eth_dev_log(eth_dev,
			"Upper interface %s is already registered. Skipping.",
			net_dev->name);
		return 0;
	}
		"Registering upper interface %s", net_dev->name);

	rc = ipa_eth_ep_register_alt_interface(eth_dev, net_dev);
	if (rc) {
@@ -505,15 +501,11 @@ int ipa_eth_ep_unregister_upper_interface(
	struct net_device *net_dev = upper_eth_dev->net_dev;
	struct ipa_eth_device *eth_dev = upper_eth_dev->eth_dev;

	ipa_eth_dev_log(eth_dev,
		"Unegistering upper interface %s", net_dev->name);
	if (!upper_eth_dev->registered)
		return 0;

	if (!upper_eth_dev->registered) {
	ipa_eth_dev_log(eth_dev,
			"Upper interface %s is already unregistered. Skipping.",
			net_dev->name);
		return 0;
	}
		"Unegistering upper interface %s", net_dev->name);

	rc = ipa_eth_ep_deregister_alt_interface(eth_dev, net_dev);
	if (rc) {
+3 −9
Original line number Diff line number Diff line
@@ -108,19 +108,13 @@ struct ipa_eth_upper_device {
	struct net_device *net_dev;
	struct ipa_eth_device *eth_dev;

	bool linked; /* upper device is linked to real device */
	bool watching; /* registered netdevice notifier */
	bool up; /* interface is up */
	bool registered; /* registered with IPA */

	struct notifier_block netdevice_nb;

	struct kref refcount;
};

struct ipa_eth_device_private {
	struct ipa_eth_device *eth_dev;

	struct mutex upper_mutex;
	struct list_head upper_devices;

	struct notifier_block panic_nb;
@@ -199,8 +193,8 @@ int ipa_eth_offload_complete_reset(struct ipa_eth_device *eth_dev, void *data);

int ipa_eth_net_register_driver(struct ipa_eth_net_driver *nd);
void ipa_eth_net_unregister_driver(struct ipa_eth_net_driver *nd);
int ipa_eth_net_watch_upper(struct ipa_eth_device *eth_dev);
int ipa_eth_net_unwatch_upper(struct ipa_eth_device *eth_dev);
int ipa_eth_net_register_upper(struct ipa_eth_device *eth_dev);
int ipa_eth_net_unregister_upper(struct ipa_eth_device *eth_dev);

int ipa_eth_net_open_device(struct ipa_eth_device *eth_dev);
void ipa_eth_net_close_device(struct ipa_eth_device *eth_dev);
+176 −185
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@
 * GNU General Public License for more details.
 */

#include <linux/rtnetlink.h>

#include "ipa_eth_i.h"

#define ipa_eth_nd_op(eth_dev, op, args...) (eth_dev->nd->ops->op(args))
@@ -87,282 +89,247 @@ void ipa_eth_net_unregister_driver(struct ipa_eth_net_driver *nd)
	ipa_eth_bus_unregister_driver(nd);
}

static inline bool __is_netdev_link_up(struct ipa_eth_device *eth_dev)
{
	return netif_carrier_ok(eth_dev->net_dev);
}

static inline bool __is_netdev_iface_up(struct ipa_eth_device *eth_dev)
{
	return !!(eth_dev->net_dev->flags & IFF_UP);
}

/* Event handler for netdevice events from upper interfaces */
static int ipa_eth_net_upper_event(struct notifier_block *nb,
static int ipa_eth_net_upper_event(
	struct ipa_eth_upper_device *upper_dev,
	unsigned long event, void *ptr)
{
	int rc;
	struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
	struct ipa_eth_upper_device *upper_dev =
			container_of(nb,
				struct ipa_eth_upper_device, netdevice_nb);
	struct ipa_eth_device *eth_dev = upper_dev->eth_dev;

	if (net_dev != upper_dev->net_dev)
		return NOTIFY_DONE;
	ASSERT_RTNL();

	ipa_eth_dev_log(eth_dev,
			"Received netdev event %s (0x%04lx) for %s",
			ipa_eth_net_device_event_name(event), event,
			net_dev->name);
			upper_dev->net_dev->name);

	switch (event) {
	case NETDEV_UP:
		rc = ipa_eth_ep_register_upper_interface(upper_dev);
		if (rc)
			ipa_eth_dev_err(eth_dev, "Failed to register upper");
		upper_dev->up = true;
		break;
	case NETDEV_DOWN:
		rc = ipa_eth_ep_unregister_upper_interface(upper_dev);
		if (rc)
			ipa_eth_dev_err(eth_dev, "Failed to register upper");
		upper_dev->up = false;
		break;
	default:
		break;
	}

		return NOTIFY_DONE;
	}

static void __ipa_eth_upper_release(struct kref *ref)
{
	struct ipa_eth_upper_device *upper_dev =
		container_of(ref, struct ipa_eth_upper_device, refcount);

	list_del(&upper_dev->upper_list);
	kzfree(upper_dev);
}

static inline void kref_get_upper(struct ipa_eth_upper_device *upper_dev)
{
	kref_get(&upper_dev->refcount);
}

static inline int kref_put_upper(struct ipa_eth_upper_device *upper_dev)
{
	return kref_put(&upper_dev->refcount, __ipa_eth_upper_release);
}

static int ipa_eth_net_watch_upper_device(
		struct ipa_eth_upper_device *upper_dev)
{
	int rc;
	struct ipa_eth_device *eth_dev = upper_dev->eth_dev;

	if (upper_dev->watching)
		return 0;

	ipa_eth_dev_log(eth_dev,
			"Going to watch upper device %s",
			upper_dev->net_dev->name);

	rc = register_netdevice_notifier(&upper_dev->netdevice_nb);
	if (rc) {
		ipa_eth_dev_err(eth_dev,
			"Failed to register with netdevice notifier");
		return rc;
	}

	upper_dev->watching = true;

	kref_get_upper(upper_dev);

	return 0;
}

static int ipa_eth_net_unwatch_upper_device_unsafe(
		struct ipa_eth_upper_device *upper_dev)
{
	int rc;
	struct ipa_eth_device *eth_dev = upper_dev->eth_dev;

	if (!upper_dev->watching)
		return 0;

	rc = unregister_netdevice_notifier(&upper_dev->netdevice_nb);
	if (rc) {
		ipa_eth_dev_err(eth_dev,
			"Failed to unregister with netdevice notifier");
		return rc;
	}

	ipa_eth_dev_log(eth_dev, "Stopped watching upper device %s",
			upper_dev->net_dev->name);

	upper_dev->watching = false;

	/* kref_put_upper() unlinks upper_dev from upper_devices list before
	 * freeing it, causing this function unsafe to use during linked list
	 * iteration.
	/* Register/unregister the upper interface from refresh wq to avoid
	 * race conditions.
	 */
	kref_put_upper(upper_dev);
	ipa_eth_device_refresh_sched(eth_dev);

	return rc;
	return NOTIFY_OK;
}

static int ipa_eth_net_unwatch_unlinked(struct ipa_eth_device *eth_dev)
static inline struct ipa_eth_upper_device *ipa_eth_search_upper(
		struct ipa_eth_device *eth_dev,
		struct net_device *net_dev)
{
	int rc = 0;
	struct ipa_eth_device_private *dev_priv = eth_dev->ipa_priv;
	struct ipa_eth_upper_device *upper_dev = NULL;
	struct ipa_eth_upper_device *tmp = NULL;

	mutex_lock(&dev_priv->upper_mutex);
	struct ipa_eth_device_private *dev_priv = eth_dev->ipa_priv;

	list_for_each_entry_safe(upper_dev, tmp,
					&dev_priv->upper_devices, upper_list) {
		if (upper_dev->linked)
			continue;
	ASSERT_RTNL();

		rc |= ipa_eth_net_unwatch_upper_device_unsafe(upper_dev);
	list_for_each_entry(upper_dev, &dev_priv->upper_devices, upper_list) {
		if (upper_dev->net_dev == net_dev)
			return upper_dev;
	}

	mutex_unlock(&dev_priv->upper_mutex);

	return rc;
	return NULL;
}

int ipa_eth_net_watch_upper(struct ipa_eth_device *eth_dev)
/* Register all active upper interfaces, unregistering ones that are down. */
int ipa_eth_net_register_upper(struct ipa_eth_device *eth_dev)
{
	int rc = 0;
	struct ipa_eth_device_private *dev_priv = eth_dev->ipa_priv;
	struct ipa_eth_upper_device *upper_dev = NULL;

	/* We cannot acquire rtnl_mutex because we need to subsequently call
	 * register_netdevice_notifier.
	ipa_eth_dev_log(eth_dev, "Registering all active upper devices");

	/* The list and its entries are updated by events from netdevice
	 * notifier which will hold rtnl mutex; use same mutex to synchronize.
	 */
	mutex_lock(&dev_priv->upper_mutex);
	rtnl_lock();

	list_for_each_entry(upper_dev, &dev_priv->upper_devices, upper_list) {
		if (!upper_dev->linked)
			continue;

		rc = ipa_eth_net_watch_upper_device(upper_dev);
		if (rc)
			break;
	}

	if (rc) {
		list_for_each_entry_continue_reverse(upper_dev,
				&dev_priv->upper_devices, upper_list) {
			/* Since we are unwatching only linked devices, they
			 * will not be removed from the linked list, so we
			 * do not need to use safe iteration for linked list.
		/* Register upper interfaces that are up, unregister ones that
		 * are down.
		 */
			if (upper_dev->linked)
				ipa_eth_net_unwatch_upper_device_unsafe(
						upper_dev);
		}
		if (upper_dev->up && !upper_dev->registered)
			rc |= ipa_eth_ep_register_upper_interface(upper_dev);
		else if (upper_dev->registered && !upper_dev->up)
			rc |= ipa_eth_ep_unregister_upper_interface(upper_dev);
	}

	mutex_unlock(&dev_priv->upper_mutex);
	rtnl_unlock();

	if (ipa_eth_net_unwatch_unlinked(eth_dev)) {
	if (rc)
		ipa_eth_dev_err(eth_dev,
			"Failed to unwatch one or more unliked upper devices");
	}
			"Failed to {un}register one or more upper devices");

	return rc;
}

int ipa_eth_net_unwatch_upper(struct ipa_eth_device *eth_dev)
/* Unregister all upper interfaces */
int ipa_eth_net_unregister_upper(struct ipa_eth_device *eth_dev)
{
	int rc = 0;
	struct ipa_eth_device_private *dev_priv = eth_dev->ipa_priv;
	struct ipa_eth_upper_device *upper_dev = NULL;
	struct ipa_eth_upper_device *tmp = NULL;

	mutex_lock(&dev_priv->upper_mutex);
	ipa_eth_dev_log(eth_dev, "Unregistering all upper devices");

	list_for_each_entry_safe(upper_dev, tmp,
					&dev_priv->upper_devices, upper_list)
		rc |= ipa_eth_net_unwatch_upper_device_unsafe(upper_dev);
	rtnl_lock();

	list_for_each_entry(upper_dev, &dev_priv->upper_devices, upper_list) {
		if (likely(upper_dev->registered))
			rc |= ipa_eth_ep_unregister_upper_interface(upper_dev);

	}

	rtnl_unlock();

	if (rc)
		ipa_eth_dev_err(eth_dev,
			"Failed to unwatch one or more upper devices");

	mutex_unlock(&dev_priv->upper_mutex);
			"Failed to unregister one or more upper devices");

	return rc;
}

static int ipa_eth_net_link_upper(struct ipa_eth_device *eth_dev,
	struct net_device *upper_net_dev)
	struct net_device *net_dev)
{
	int rc = 0;
	struct ipa_eth_upper_device *upper_dev;
	struct ipa_eth_device_private *dev_priv = eth_dev->ipa_priv;

	ASSERT_RTNL();

	ipa_eth_dev_log(eth_dev,
		"Linking upper interface %s", upper_net_dev->name);
		"Linking upper interface %s", net_dev->name);

	upper_dev = kzalloc(sizeof(*upper_dev), GFP_KERNEL);
	if (!upper_dev)
	if (!upper_dev) {
		ipa_eth_dev_err(eth_dev, "Failed to alloc upper dev");
		return -ENOMEM;
	}

	kref_init(&upper_dev->refcount);

	upper_dev->linked = true;
	upper_dev->eth_dev = eth_dev;
	upper_dev->net_dev = upper_net_dev;
	upper_dev->netdevice_nb.notifier_call = ipa_eth_net_upper_event;
	upper_dev->net_dev = net_dev;

	mutex_lock(&dev_priv->upper_mutex);
	list_add(&upper_dev->upper_list, &dev_priv->upper_devices);
	mutex_unlock(&dev_priv->upper_mutex);

	/* We cannot call register_netdevice_notifier() from here since we
	 * are already holding rtnl_mutex. Schedule a device refresh for the
	 * offload sub-system workqueue to re-scan upper list and register for
	 * notifications.
	/* Fetch link status for unlike scenarios where the upper interface is
	 * being linked from ipa_eth_net_event_register().
	 */
	ipa_eth_device_refresh_sched(eth_dev);
	upper_dev->up = __is_netdev_iface_up(eth_dev);

	return rc;
	list_add(&upper_dev->upper_list, &dev_priv->upper_devices);

	return 0;
}

static int ipa_eth_net_unlink_upper(struct ipa_eth_device *eth_dev,
	struct net_device *upper_net_dev)
static int __ipa_eth_net_unlink_upper(struct ipa_eth_upper_device *upper_dev)
{
	int rc = -ENODEV;
	struct ipa_eth_device_private *dev_priv = eth_dev->ipa_priv;
	struct ipa_eth_upper_device *upper_dev = NULL;
	ASSERT_RTNL();

	ipa_eth_dev_log(eth_dev,
		"Unlinking upper interface %s", upper_net_dev->name);

	mutex_lock(&dev_priv->upper_mutex);
	ipa_eth_dev_log(upper_dev->eth_dev,
		"Unlinking upper interface %s", upper_dev->net_dev->name);

	list_for_each_entry(upper_dev, &dev_priv->upper_devices, upper_list) {
		if (upper_dev->net_dev == upper_net_dev) {
			upper_dev->linked = false;

			/* We can free upper_dev only if the refresh wq has
			 * already unregistered the netdevice notifier.
	/* Even though we would have received a NETDEV_DOWN event prior to
	 * receiving a PRECHANGEUPPER(unlink) event, a device refresh may not
	 * have completed by this time; explicitly unregister the upper device.
	 */
			kref_put_upper(upper_dev);
	ipa_eth_ep_unregister_upper_interface(upper_dev);

			rc = 0;
			break;
		}
	list_del(&upper_dev->upper_list);
	kzfree(upper_dev);

	return 0;
}

	mutex_unlock(&dev_priv->upper_mutex);
static int ipa_eth_net_unlink_upper(struct ipa_eth_device *eth_dev,
	struct net_device *net_dev)
{
	struct ipa_eth_upper_device *upper_dev =
			ipa_eth_search_upper(eth_dev, net_dev);

	ipa_eth_device_refresh_sched(eth_dev);
	if (!upper_dev) {
		ipa_eth_dev_bug(eth_dev,
			"Failed to find upper dev %s", net_dev->name);
		return -ENODEV;
	}

	return rc;
	return __ipa_eth_net_unlink_upper(upper_dev);
}

static bool ipa_eth_net_update_link(struct ipa_eth_device *eth_dev)
{
	return netif_carrier_ok(eth_dev->net_dev) ?
	return __is_netdev_link_up(eth_dev) ?
		!test_and_set_bit(IPA_ETH_IF_ST_LOWER_UP, &eth_dev->if_state) :
		test_and_clear_bit(IPA_ETH_IF_ST_LOWER_UP, &eth_dev->if_state);

}

/* Use the register event to detect any previously registered upper devices. */
static bool ipa_eth_net_event_register(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr)
{
	struct net_device *udev;
	struct list_head *iter;

	bool refresh_needed = false;
	struct net_device *net_dev = eth_dev->net_dev;

	/* In the unlikely scenario where an upper interface was created before
	 * we registered with netdevice notifier, manually link the upper dev to
	 * eth_dev.
	 */
	netdev_for_each_upper_dev_rcu(net_dev, udev, iter) {
		if (!ipa_eth_net_link_upper(eth_dev, udev))
			refresh_needed = true;
	}

	return refresh_needed;
}

static bool ipa_eth_net_event_unregister(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr)
{
	struct ipa_eth_device_private *dev_priv = eth_dev->ipa_priv;
	struct ipa_eth_upper_device *upper_dev = NULL;
	struct ipa_eth_upper_device *tmp = NULL;

	/* Any upper interfaces discovered previously are expected to have been
	 * unlinked before the real dev deregisters with network sub-system.
	 * Since offload sub-system never deregisters with netdevice notifier
	 * until real dev is removed from the bus, and as the network driver is
	 * expected to keep registered with network sub-system until a bus level
	 * removal happens for the device, we really should not see any upper
	 * interfaces in upper_devices list at this point.
	 */
	list_for_each_entry_safe(upper_dev, tmp,
				&dev_priv->upper_devices, upper_list) {
		ipa_eth_dev_bug(eth_dev,
			"Upper interface %s is unexpected",
			upper_dev->net_dev->name);

		__ipa_eth_net_unlink_upper(upper_dev);
	}

	return false;
}

static bool ipa_eth_net_event_up(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr)
{
@@ -389,7 +356,7 @@ static bool ipa_eth_net_event_pre_change_upper(struct ipa_eth_device *eth_dev,
	if (!upper_info->linking)
		ipa_eth_net_unlink_upper(eth_dev, upper_info->upper_dev);

	return false;
	return true;
}

static bool ipa_eth_net_event_change_upper(struct ipa_eth_device *eth_dev,
@@ -400,7 +367,7 @@ static bool ipa_eth_net_event_change_upper(struct ipa_eth_device *eth_dev,
	if (upper_info->linking)
		ipa_eth_net_link_upper(eth_dev, upper_info->upper_dev);

	return false;
	return true;
}

typedef bool (*ipa_eth_net_event_handler)(struct ipa_eth_device *eth_dev,
@@ -409,6 +376,8 @@ typedef bool (*ipa_eth_net_event_handler)(struct ipa_eth_device *eth_dev,
/* Event handlers for netdevice events from real interface */
static ipa_eth_net_event_handler
		ipa_eth_net_event_handlers[IPA_ETH_NET_DEVICE_MAX_EVENTS] = {
	[NETDEV_REGISTER] = ipa_eth_net_event_register,
	[NETDEV_UNREGISTER] = ipa_eth_net_event_unregister,
	[NETDEV_UP] = ipa_eth_net_event_up,
	[NETDEV_DOWN] = ipa_eth_net_event_down,
	[NETDEV_CHANGE] = ipa_eth_net_event_change,
@@ -420,10 +389,24 @@ static ipa_eth_net_event_handler
static int ipa_eth_net_device_event(struct notifier_block *nb,
	unsigned long event, void *ptr)
{
	struct ipa_eth_upper_device *upper_dev;
	struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
	struct ipa_eth_device *eth_dev = container_of(nb,
				struct ipa_eth_device, netdevice_nb);

	/* We re-use the notifier registered for real dev to also monitor for
	 * events from its upper interfaces. Once an upper interface is detected
	 * through CHANGEUPPER(linking) event from real dev and added to the
	 * upper_devices list, we let ipa_eth_net_upper_event() receive future
	 * events for the interface. The upper interface is similarly removed
	 * from the list on receiving PRECHANGEUPPER(!linking) event. Although
	 * this method will prevent us from handling REGISTER/UNREGISTER events,
	 * those can be deduced from {PRE}CHANGEUPPER if needed.
	 */
	upper_dev = ipa_eth_search_upper(eth_dev, net_dev);
	if (upper_dev)
		return ipa_eth_net_upper_event(upper_dev, event, ptr);

	if (net_dev != eth_dev->net_dev)
		return NOTIFY_DONE;

@@ -440,9 +423,12 @@ static int ipa_eth_net_device_event(struct notifier_block *nb,
		 */
		if (refresh_needed)
			ipa_eth_device_refresh_sched(eth_dev);
	} else {
		ipa_eth_dev_err(eth_dev, "Event number out of bounds");
		return NOTIFY_DONE;
	}

	return NOTIFY_DONE;
	return NOTIFY_OK;
}

int ipa_eth_net_open_device(struct ipa_eth_device *eth_dev)
@@ -462,6 +448,11 @@ int ipa_eth_net_open_device(struct ipa_eth_device *eth_dev)
		goto err_net_dev;
	}

	/* We need to register with netdevice notifier early in the real dev
	 * lifetime as we need to also use the same registration to discover all
	 * its upper devices using CHANGEUPPER events, and further accept any
	 * events from those upper devices too.
	 */
	eth_dev->netdevice_nb.notifier_call = ipa_eth_net_device_event;
	rc = register_netdevice_notifier(&eth_dev->netdevice_nb);
	if (rc) {