Loading drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom_ipa.c +8 −0 Original line number Diff line number Diff line Loading @@ -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; } Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c +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 Loading Loading @@ -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"); Loading Loading @@ -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); Loading Loading @@ -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; } Loading Loading @@ -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; Loading @@ -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; } Loading @@ -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); } /* Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_ep.c +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 Loading Loading @@ -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 = ð_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(ð_dev->tx_channels) * 2; Loading @@ -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 = ð_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(ð_dev->rx_channels) * 2; Loading @@ -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(ð_dev->ipa_tx_intf, 0, sizeof(eth_dev->ipa_tx_intf)); kzfree(eth_dev->ipa_rx_intf.prop); memset(ð_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 = ð_dev->ipa_tx_intf; struct ipa_rx_intf *rx_intf = ð_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 = ð_dev->ipa_tx_intf; struct ipa_rx_intf *rx_intf = ð_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 Loading @@ -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 Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_i.h +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 Loading Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_net.c +323 −19 Original line number Diff line number Diff line Loading @@ -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, ð_dev->if_state) : test_and_clear_bit(IPA_ETH_IF_ST_LOWER_UP, ð_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, ð_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, ð_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, ð_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, ð_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, ð_dev->if_state) : test_and_clear_bit(IPA_ETH_IF_ST_LOWER_UP, ð_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) { Loading @@ -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 Loading
drivers/net/ethernet/aquantia/atlantic-fwd/atl_qcom_ipa.c +8 −0 Original line number Diff line number Diff line Loading @@ -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; } Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c +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 Loading Loading @@ -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"); Loading Loading @@ -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); Loading Loading @@ -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; } Loading Loading @@ -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; Loading @@ -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; } Loading @@ -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); } /* Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_ep.c +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 Loading Loading @@ -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 = ð_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(ð_dev->tx_channels) * 2; Loading @@ -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 = ð_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(ð_dev->rx_channels) * 2; Loading @@ -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(ð_dev->ipa_tx_intf, 0, sizeof(eth_dev->ipa_tx_intf)); kzfree(eth_dev->ipa_rx_intf.prop); memset(ð_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 = ð_dev->ipa_tx_intf; struct ipa_rx_intf *rx_intf = ð_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 = ð_dev->ipa_tx_intf; struct ipa_rx_intf *rx_intf = ð_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 Loading @@ -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 Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_i.h +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 Loading Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading
drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_net.c +323 −19 Original line number Diff line number Diff line Loading @@ -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, ð_dev->if_state) : test_and_clear_bit(IPA_ETH_IF_ST_LOWER_UP, ð_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, ð_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, ð_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, ð_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, ð_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, ð_dev->if_state) : test_and_clear_bit(IPA_ETH_IF_ST_LOWER_UP, ð_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) { Loading @@ -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