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

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

Merge "net: aquantia: Send MACSEC events to IPA offload sub-system"

parents 4e504ae8 d9df6479
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -119,6 +119,14 @@ static int atl_ipa_fwd_notification(struct notifier_block *nb,
		ipa_eth_device_notify(ai_dev->eth_dev,
				      IPA_ETH_DEV_RESET_COMPLETE, NULL);
		break;
	case ATL_FWD_NOTIFY_MACSEC_ON:
		ipa_eth_device_notify(ai_dev->eth_dev,
				      IPA_ETH_DEV_ADD_MACSEC_IF, data);
		break;
	case ATL_FWD_NOTIFY_MACSEC_OFF:
		ipa_eth_device_notify(ai_dev->eth_dev,
				      IPA_ETH_DEV_DEL_MACSEC_IF, data);
		break;
	default:
		return NOTIFY_DONE;
	}
+34 −7
Original line number Diff line number Diff line
/* Copyright (c) 2019 The Linux Foundation. All rights reserved.
/* Copyright (c) 2019-2020 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
@@ -190,6 +190,13 @@ static int ipa_eth_start_device(struct ipa_eth_device *eth_dev)
		return rc;
	}

	/* We cannot register the interface during offload init phase because
	 * it will cause IPACM to install IPA filter rules when it receives a
	 * link-up netdev event, even if offload path is not started and/or no
	 * ECM_CONNECT event is received from the driver. Since installing IPA
	 * filter rules while offload path is stopped can cause DL data stall,
	 * register the interface only after the offload path is started.
	 */
	rc = ipa_eth_ep_register_interface(eth_dev);
	if (rc) {
		ipa_eth_dev_err(eth_dev, "Failed to register EP interface");
@@ -299,12 +306,20 @@ static void ipa_eth_device_refresh(struct ipa_eth_device *eth_dev)
			return;
		}

		if (ipa_eth_net_watch_upper(eth_dev))
			ipa_eth_dev_err(eth_dev,
					"Failed to watch upper interfaces");

		if (ipa_eth_pm_vote_bw(eth_dev))
			ipa_eth_dev_err(eth_dev,
					"Failed to vote for required BW");
	} else {
		ipa_eth_dev_log(eth_dev, "Start is disallowed for the device");

		if (ipa_eth_net_unwatch_upper(eth_dev))
			ipa_eth_dev_err(eth_dev,
					"Failed to unwatch upper interfaces");

		if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) {
			IPA_ACTIVE_CLIENTS_INC_SIMPLE();
			ipa_eth_stop_device(eth_dev);
@@ -439,7 +454,7 @@ int ipa_eth_device_notify(struct ipa_eth_device *eth_dev,
		rc = ipa_eth_device_complete_reset(eth_dev, data);
		break;
	default:
		ipa_eth_dev_bug(eth_dev, "Unknown event");
		ipa_eth_dev_log(eth_dev, "Skipped event processing");
		break;
	}

@@ -504,16 +519,23 @@ struct ipa_eth_device *ipa_eth_alloc_device(
	struct ipa_eth_net_driver *nd)
{
	struct ipa_eth_device *eth_dev;
	struct ipa_eth_device_private *ipa_priv;

	if (!dev || !nd) {
		ipa_eth_err("Invalid device or net driver");
		return NULL;
	}

	eth_dev = devm_kzalloc(dev, sizeof(*eth_dev), GFP_KERNEL);
	eth_dev = kzalloc(sizeof(*eth_dev), GFP_KERNEL);
	if (!eth_dev)
		return NULL;

	ipa_priv = kzalloc(sizeof(*ipa_priv), GFP_KERNEL);
	if (!ipa_priv) {
		kfree(eth_dev);
		return NULL;
	}

	eth_dev->dev = dev;
	eth_dev->nd = nd;

@@ -531,6 +553,13 @@ struct ipa_eth_device *ipa_eth_alloc_device(

	eth_dev->init = eth_dev->start = !ipa_eth_noauto;

	/* Initialize private data */

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

	eth_dev->ipa_priv = ipa_priv;

	return eth_dev;
}

@@ -541,10 +570,8 @@ struct ipa_eth_device *ipa_eth_alloc_device(
 */
void ipa_eth_free_device(struct ipa_eth_device *eth_dev)
{
	struct device *dev = eth_dev->dev;

	memset(eth_dev, 0, sizeof(*eth_dev));
	devm_kfree(dev, eth_dev);
	kzfree(eth_dev->ipa_priv);
	kzfree(eth_dev);
}

/*
+213 −30
Original line number Diff line number Diff line
/* Copyright (c) 2019 The Linux Foundation. All rights reserved.
/* Copyright (c) 2019-2020 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
@@ -228,11 +228,16 @@ static void ipa_eth_ep_init_tx_props(

static int ipa_eth_ep_init_tx_intf(
		struct ipa_eth_device *eth_dev,
		struct ipa_tx_intf *tx_intf,
		bool vlan_mode)
{
	u32 num_props;
	struct ipa_eth_channel *ch;
	struct ipa_tx_intf *tx_intf = &eth_dev->ipa_tx_intf;

	if (tx_intf->prop) {
		ipa_eth_dev_bug(eth_dev, "IPA interface Tx prop is not empty");
		return -EFAULT;
	}

	/* one each for IPv4 and IPv6 */
	num_props = __list_size(&eth_dev->tx_channels) * 2;
@@ -257,11 +262,16 @@ static int ipa_eth_ep_init_tx_intf(

static int ipa_eth_ep_init_rx_intf(
		struct ipa_eth_device *eth_dev,
		struct ipa_rx_intf *rx_intf,
		bool vlan_mode)
{
	u32 num_props;
	struct ipa_eth_channel *ch;
	struct ipa_rx_intf *rx_intf = &eth_dev->ipa_rx_intf;

	if (rx_intf->prop) {
		ipa_eth_dev_bug(eth_dev, "IPA interface Rx prop is not empty");
		return -EFAULT;
	}

	/* one each for IPv4 and IPv6 */
	num_props = __list_size(&eth_dev->rx_channels) * 2;
@@ -284,51 +294,158 @@ static int ipa_eth_ep_init_rx_intf(
	return 0;
}

/**
 * ipa_eth_ep_register_interface() - Set Rx and Tx properties and register the
 *                                   interface with IPA
 * @eth_dev: Offload device to register
 *
 * Register a logical interface with IPA. The API expects netdev and channels
 * allocated prior to being called.
 *
 * Return: 0 on success, negative errno code otherwise
 */
int ipa_eth_ep_register_interface(struct ipa_eth_device *eth_dev)
static void ipa_eth_ep_deinit_interfaces(struct ipa_eth_device *eth_dev)
{
	kzfree(eth_dev->ipa_tx_intf.prop);
	memset(&eth_dev->ipa_tx_intf, 0, sizeof(eth_dev->ipa_tx_intf));

	kzfree(eth_dev->ipa_rx_intf.prop);
	memset(&eth_dev->ipa_rx_intf, 0, sizeof(eth_dev->ipa_rx_intf));

}

static int ipa_eth_ep_init_interfaces(struct ipa_eth_device *eth_dev)
{
	int rc;
	bool vlan_mode;
	struct ipa_tx_intf tx_intf;
	struct ipa_rx_intf rx_intf;

	memset(&tx_intf, 0, sizeof(tx_intf));
	memset(&rx_intf, 0, sizeof(rx_intf));
	if (eth_dev->ipa_rx_intf.num_props || eth_dev->ipa_tx_intf.num_props) {
		ipa_eth_dev_err(eth_dev, "Interface properties already exist");
		return -EFAULT;
	}

	rc = ipa3_is_vlan_mode(IPA_VLAN_IF_ETH, &vlan_mode);
	if (rc) {
		ipa_eth_dev_err(eth_dev, "Could not determine IPA VLAN mode");
		goto free_and_exit;
		return rc;
	}

	rc = ipa_eth_ep_init_tx_intf(eth_dev, &tx_intf, vlan_mode);
	rc = ipa_eth_ep_init_tx_intf(eth_dev, vlan_mode);
	if (rc)
		goto free_and_exit;

	rc = ipa_eth_ep_init_rx_intf(eth_dev, &rx_intf, vlan_mode);
	rc = ipa_eth_ep_init_rx_intf(eth_dev, vlan_mode);
	if (rc)
		goto free_and_exit;

	rc = ipa_register_intf(eth_dev->net_dev->name, &tx_intf, &rx_intf);
	if (!rc)
		ipa_eth_send_msg_connect(eth_dev);
	return 0;

free_and_exit:
	kzfree(tx_intf.prop);
	kzfree(rx_intf.prop);
	ipa_eth_ep_deinit_interfaces(eth_dev);

	return rc;
}

static int ipa_eth_ep_deregister_ipa_intf(
	struct net_device *net_dev)
{
	int rc;

	ipa_eth_log("Deregistering IPA intf %s", net_dev->name);

	rc = ipa_deregister_intf(net_dev->name);
	if (rc) {
		ipa_eth_err("Failed to deregister IPA intf %s", net_dev->name);
		return rc;
	}

	rc = ipa_eth_send_msg_disconnect(net_dev);
	if (rc)
		ipa_eth_err("Failed to send disconnect message", net_dev->name);

	return rc;
}

static int ipa_eth_ep_register_ipa_intf(
	struct net_device *net_dev,
	struct ipa_tx_intf *tx_intf,
	struct ipa_rx_intf *rx_intf)
{
	int rc;

	ipa_eth_log("Registering IPA intf %s", net_dev->name);

	rc = ipa_register_intf(net_dev->name, tx_intf, rx_intf);
	if (rc) {
		ipa_eth_err("Failed to register IPA intf %s", net_dev->name);
		return rc;
	}

	rc = ipa_eth_send_msg_connect(net_dev);
	if (rc) {
		ipa_eth_err("Failed to send connect message", net_dev->name);
		(void) ipa_eth_ep_deregister_ipa_intf(net_dev);
	}

	return rc;
}

static int ipa_eth_ep_register_alt_interface(
		struct ipa_eth_device *eth_dev,
		struct net_device *net_dev)
{
	struct ipa_tx_intf *tx_intf = &eth_dev->ipa_tx_intf;
	struct ipa_rx_intf *rx_intf = &eth_dev->ipa_rx_intf;

	return ipa_eth_ep_register_ipa_intf(net_dev, tx_intf, rx_intf);
}

static int ipa_eth_ep_deregister_alt_interface(
		struct ipa_eth_device *eth_dev,
		struct net_device *net_dev)
{
	return ipa_eth_ep_deregister_ipa_intf(net_dev);
}

static int __ipa_eth_ep_register_interface(
	struct ipa_eth_device *eth_dev)
{
	int rc;
	struct net_device *net_dev = eth_dev->net_dev;
	struct ipa_tx_intf *tx_intf = &eth_dev->ipa_tx_intf;
	struct ipa_rx_intf *rx_intf = &eth_dev->ipa_rx_intf;

	ipa_eth_dev_log(eth_dev, "Registering interface %s", net_dev->name);

	rc = ipa_eth_ep_register_ipa_intf(net_dev, tx_intf, rx_intf);
	if (rc) {
		ipa_eth_err("Failed to register IPA interface");
		return rc;
	}

	return 0;
}

/**
 * ipa_eth_ep_register_interface() - Set Rx and Tx properties and register the
 *                                   interface with IPA
 * @eth_dev: Offload device to register
 *
 * Register a logical interface with IPA. The API expects netdev and channels
 * allocated prior to being called.
 *
 * Return: 0 on success, negative errno code otherwise
 */
int ipa_eth_ep_register_interface(struct ipa_eth_device *eth_dev)
{
	int rc;

	ipa_eth_dev_log(eth_dev, "Registering interface with IPA driver");

	rc = ipa_eth_ep_init_interfaces(eth_dev);
	if (rc)
		return rc;

	rc = __ipa_eth_ep_register_interface(eth_dev);
	if (rc) {
		ipa_eth_dev_err(eth_dev, "Failed to register IPA interface");
		ipa_eth_ep_deinit_interfaces(eth_dev);
		return rc;
	}

	return 0;
}

/**
 * ipa_eth_ep_unregister_interface() - Unregister a previously registered
 *                                     interface
@@ -338,13 +455,79 @@ int ipa_eth_ep_unregister_interface(struct ipa_eth_device *eth_dev)
{
	int rc;

	rc = ipa_deregister_intf(eth_dev->net_dev->name);
	if (!rc)
		ipa_eth_send_msg_disconnect(eth_dev);
	ipa_eth_dev_log(eth_dev, "Unregistering interface from IPA driver");

	rc = ipa_eth_ep_deregister_ipa_intf(eth_dev->net_dev);
	if (rc) {
		ipa_eth_dev_err(eth_dev,
			"Failed to de-register one or more interfaces");
		return rc;
	}

	ipa_eth_ep_deinit_interfaces(eth_dev);

	return 0;
}

int ipa_eth_ep_register_upper_interface(
	struct ipa_eth_upper_device *upper_eth_dev)
{
	int rc;
	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) {
		ipa_eth_dev_log(eth_dev,
			"Upper interface %s is already registered. Skipping.",
			net_dev->name);
		return 0;
	}

	rc = ipa_eth_ep_register_alt_interface(eth_dev, net_dev);
	if (rc) {
		ipa_eth_dev_err(eth_dev,
			"Failed to register upper interface %s", net_dev->name);
		return rc;
	}

	upper_eth_dev->registered = true;

	return 0;
}

int ipa_eth_ep_unregister_upper_interface(
	struct ipa_eth_upper_device *upper_eth_dev)
{
	int rc;
	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) {
		ipa_eth_dev_log(eth_dev,
			"Upper interface %s is already unregistered. Skipping.",
			net_dev->name);
		return 0;
	}

	rc = ipa_eth_ep_deregister_alt_interface(eth_dev, net_dev);
	if (rc) {
		ipa_eth_dev_err(eth_dev,
			"Failed to unregister upper interface %s",
			net_dev->name);
		return rc;
	}

	upper_eth_dev->registered = false;

	return 0;
}

/**
 * ipa_eth_ep_init_ctx - Initialize IPA endpoint context for a channel
 * @ch: Channel for which EP ctx need to be initialized
+36 −3
Original line number Diff line number Diff line
/* Copyright (c) 2019 The Linux Foundation. All rights reserved.
/* Copyright (c) 2019-2020 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
@@ -36,6 +36,8 @@
#define IPA_ETH_PFDEV (ipa3_ctx ? ipa3_ctx->pdev : NULL)
#define IPA_ETH_SUBSYS "ipa_eth"

#define IPA_ETH_NET_DEVICE_MAX_EVENTS (NETDEV_CHANGE_TX_QUEUE_LEN + 1)

enum ipa_eth_states {
	IPA_ETH_ST_READY,
	IPA_ETH_ST_UC_READY,
@@ -100,6 +102,26 @@ enum ipa_eth_dev_flags {
		edev->net_dev->name : "<unpaired>"), \
		## args)

struct ipa_eth_upper_device {
	struct list_head upper_list;

	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 registered; /* registered with IPA */

	struct notifier_block netdevice_nb;

	struct kref refcount;
};

struct ipa_eth_device_private {
	struct mutex upper_mutex;
	struct list_head upper_devices;
};

struct ipa_eth_bus {
	struct list_head bus_list;

@@ -173,6 +195,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_open_device(struct ipa_eth_device *eth_dev);
void ipa_eth_net_close_device(struct ipa_eth_device *eth_dev);
@@ -181,8 +205,15 @@ int ipa_eth_net_save_regs(struct ipa_eth_device *eth_dev);

int ipa_eth_ep_init_headers(struct ipa_eth_device *eth_dev);
int ipa_eth_ep_deinit_headers(struct ipa_eth_device *eth_dev);

int ipa_eth_ep_register_interface(struct ipa_eth_device *eth_dev);
int ipa_eth_ep_unregister_interface(struct ipa_eth_device *eth_dev);

int ipa_eth_ep_register_upper_interface(
	struct ipa_eth_upper_device *upper_eth_dev);
int ipa_eth_ep_unregister_upper_interface(
	struct ipa_eth_upper_device *upper_eth_dev);

void ipa_eth_ep_init_ctx(struct ipa_eth_channel *ch, bool vlan_mode);
void ipa_eth_ep_deinit_ctx(struct ipa_eth_channel *ch);

@@ -202,8 +233,10 @@ int ipa_eth_uc_stats_stop(struct ipa_eth_device *eth_dev);
/* ipa_eth_utils.c APIs */

const char *ipa_eth_device_event_name(enum ipa_eth_device_event event);
int ipa_eth_send_msg_connect(struct ipa_eth_device *eth_dev);
int ipa_eth_send_msg_disconnect(struct ipa_eth_device *eth_dev);
const char *ipa_eth_net_device_event_name(unsigned long event);

int ipa_eth_send_msg_connect(struct net_device *net_dev);
int ipa_eth_send_msg_disconnect(struct net_device *net_dev);

void *ipa_eth_get_ipc_logbuf(void);
void *ipa_eth_get_ipc_logbuf_dbg(void);
+323 −19
Original line number Diff line number Diff line
@@ -87,40 +87,331 @@ void ipa_eth_net_unregister_driver(struct ipa_eth_net_driver *nd)
	ipa_eth_bus_unregister_driver(nd);
}

static int ipa_eth_net_process_event(
	struct ipa_eth_device *eth_dev,
/* Event handler for netdevice events from upper interfaces */
static int ipa_eth_net_upper_event(struct notifier_block *nb,
	unsigned long event, void *ptr)
{
	bool link_changed = false;
	bool iface_changed = false;
	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;

	link_changed =
		netif_carrier_ok(eth_dev->net_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);
	if (net_dev != upper_dev->net_dev)
		return NOTIFY_DONE;

	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);

	switch (event) {
	case NETDEV_UP:
		iface_changed = !test_and_set_bit(
					IPA_ETH_IF_ST_UP, &eth_dev->if_state);
		rc = ipa_eth_ep_register_upper_interface(upper_dev);
		if (rc)
			ipa_eth_dev_err(eth_dev, "Failed to register upper");
		break;
	case NETDEV_DOWN:
		iface_changed = test_and_clear_bit(
					IPA_ETH_IF_ST_UP, &eth_dev->if_state);
		rc = ipa_eth_ep_unregister_upper_interface(upper_dev);
		if (rc)
			ipa_eth_dev_err(eth_dev, "Failed to register upper");
		break;
	default:
		break;
	}

	/* We can not wait for refresh to complete because we are holding
	 * the rtnl mutex.
	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.
	 */
	kref_put_upper(upper_dev);

	return rc;
}

static int ipa_eth_net_unwatch_unlinked(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);

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

		rc |= ipa_eth_net_unwatch_upper_device_unsafe(upper_dev);
	}

	mutex_unlock(&dev_priv->upper_mutex);

	return rc;
}

int ipa_eth_net_watch_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.
	 */
	mutex_lock(&dev_priv->upper_mutex);

	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.
			 */
			if (upper_dev->linked)
				ipa_eth_net_unwatch_upper_device_unsafe(
						upper_dev);
		}
	}

	mutex_unlock(&dev_priv->upper_mutex);

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

	return rc;
}

int ipa_eth_net_unwatch_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);

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

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

	mutex_unlock(&dev_priv->upper_mutex);

	return rc;
}

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

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

	upper_dev = kzalloc(sizeof(*upper_dev), GFP_KERNEL);
	if (!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;

	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.
	 */
	if (link_changed || iface_changed)
	ipa_eth_device_refresh_sched(eth_dev);

	return NOTIFY_DONE;
	return rc;
}

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

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

	mutex_lock(&dev_priv->upper_mutex);

	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.
			 */
			kref_put_upper(upper_dev);

			rc = 0;
			break;
		}
	}

	mutex_unlock(&dev_priv->upper_mutex);

	ipa_eth_device_refresh_sched(eth_dev);

	return rc;
}

static bool ipa_eth_net_event_up(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr)
{
	return !test_and_set_bit(IPA_ETH_IF_ST_UP, &eth_dev->if_state);
}

static bool ipa_eth_net_event_down(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr)
{
	return test_and_clear_bit(IPA_ETH_IF_ST_UP, &eth_dev->if_state);
}

static bool ipa_eth_net_event_change(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr)
{
	return netif_carrier_ok(eth_dev->net_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);

}

static bool ipa_eth_net_event_pre_change_upper(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr)
{
	struct netdev_notifier_changeupper_info *upper_info = ptr;

	if (!upper_info->linking)
		ipa_eth_net_unlink_upper(eth_dev, upper_info->upper_dev);

	return false;
}

static bool ipa_eth_net_event_change_upper(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr)
{
	struct netdev_notifier_changeupper_info *upper_info = ptr;

	if (upper_info->linking)
		ipa_eth_net_link_upper(eth_dev, upper_info->upper_dev);

	return false;
}

typedef bool (*ipa_eth_net_event_handler)(struct ipa_eth_device *eth_dev,
		unsigned long event, void *ptr);

/* 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_UP] = ipa_eth_net_event_up,
	[NETDEV_DOWN] = ipa_eth_net_event_down,
	[NETDEV_CHANGE] = ipa_eth_net_event_change,
	[NETDEV_CHANGELOWERSTATE] = ipa_eth_net_event_change,
	[NETDEV_PRECHANGEUPPER] = ipa_eth_net_event_pre_change_upper,
	[NETDEV_CHANGEUPPER] = ipa_eth_net_event_change_upper,
};

static int ipa_eth_net_device_event(struct notifier_block *nb,
	unsigned long event, void *ptr)
{
@@ -131,9 +422,22 @@ static int ipa_eth_net_device_event(struct notifier_block *nb,
	if (net_dev != eth_dev->net_dev)
		return NOTIFY_DONE;

	ipa_eth_dev_log(eth_dev, "Received netdev event 0x%04lx", event);
	ipa_eth_dev_log(eth_dev, "Received netdev event %s (0x%04lx)",
			ipa_eth_net_device_event_name(event), event);

	if (event < IPA_ETH_NET_DEVICE_MAX_EVENTS) {
		ipa_eth_net_event_handler handler =
					ipa_eth_net_event_handlers[event];
		bool refresh_needed = handler && handler(eth_dev, event, ptr);

		/* We can not wait for refresh to complete as we are holding
		 * the rtnl mutex.
		 */
		if (refresh_needed)
			ipa_eth_device_refresh_sched(eth_dev);
	}

	return ipa_eth_net_process_event(eth_dev, event, ptr);
	return NOTIFY_DONE;
}

int ipa_eth_net_open_device(struct ipa_eth_device *eth_dev)
Loading