diff --git a/src/driver/linux_net/drivers/net/ethernet/sfc/net_driver.h b/src/driver/linux_net/drivers/net/ethernet/sfc/net_driver.h index 48bbd2d0..eeacad48 100644 --- a/src/driver/linux_net/drivers/net/ethernet/sfc/net_driver.h +++ b/src/driver/linux_net/drivers/net/ethernet/sfc/net_driver.h @@ -1291,6 +1291,7 @@ struct efx_arfs_rule { /** * struct efx_async_filter_insertion - Request to asynchronously insert a filter * @net_dev: Reference to the netdevice + * @net_dev_tracker: reference tracker entry for @net_dev * @spec: The filter to insert * @work: Workitem for this request * @rxq_index: Identifies the channel for which this request was made @@ -1298,6 +1299,7 @@ struct efx_arfs_rule { */ struct efx_async_filter_insertion { struct net_device *net_dev; + netdevice_tracker net_dev_tracker; struct efx_filter_spec spec; struct work_struct work; u16 rxq_index; diff --git a/src/driver/linux_net/drivers/net/ethernet/sfc/rx_common.c b/src/driver/linux_net/drivers/net/ethernet/sfc/rx_common.c index 37d6a394..2e5c7376 100644 --- a/src/driver/linux_net/drivers/net/ethernet/sfc/rx_common.c +++ b/src/driver/linux_net/drivers/net/ethernet/sfc/rx_common.c @@ -1845,7 +1845,7 @@ static void efx_filter_rfs_work(struct work_struct *data) /* Release references */ clear_bit(slot_idx, &efx->rps_slot_map); - dev_put(req->net_dev); + netdev_put(req->net_dev, &req->net_dev_tracker); return; } @@ -1908,7 +1908,8 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, } /* Queue the request */ - dev_hold(req->net_dev = net_dev); + req->net_dev = net_dev; + netdev_hold(req->net_dev, &req->net_dev_tracker, GFP_ATOMIC); INIT_WORK(&req->work, efx_filter_rfs_work); req->rxq_index = rxq_index; req->flow_id = flow_id; @@ -1965,4 +1966,3 @@ bool __efx_filter_rfs_expire(struct efx_channel *channel, unsigned int quota) } #endif /* CONFIG_RFS_ACCEL */ - diff --git a/src/driver/linux_net/drivers/net/ethernet/sfc/tc_encap_actions.c b/src/driver/linux_net/drivers/net/ethernet/sfc/tc_encap_actions.c index d81e31e7..a188dbf5 100644 --- a/src/driver/linux_net/drivers/net/ethernet/sfc/tc_encap_actions.c +++ b/src/driver/linux_net/drivers/net/ethernet/sfc/tc_encap_actions.c @@ -209,7 +209,8 @@ static int efx_bind_neigh(struct efx_nic *efx, EFX_TC_ERR_MSG(efx, extack, "Failed to lookup route for encap"); goto out_free; } - dev_hold(neigh->egdev = dst->dev); + netdev_hold(neigh->egdev = dst->dev, &neigh->dev_tracker, + GFP_KERNEL_ACCOUNT); neigh->ttl = ip6_dst_hoplimit(dst); n = dst_neigh_lookup(dst, &flow6.daddr); dst_release(dst); @@ -223,7 +224,8 @@ static int efx_bind_neigh(struct efx_nic *efx, EFX_TC_ERR_MSG(efx, extack, "Failed to lookup route for encap"); goto out_free; } - dev_hold(neigh->egdev = rt->dst.dev); + netdev_hold(neigh->egdev = rt->dst.dev, &neigh->dev_tracker, + GFP_KERNEL_ACCOUNT); neigh->ttl = ip4_dst_hoplimit(&rt->dst); n = dst_neigh_lookup(&rt->dst, &flow4.daddr); ip_rt_put(rt); @@ -233,7 +235,7 @@ static int efx_bind_neigh(struct efx_nic *efx, if (!n) { rc = -ENETUNREACH; EFX_TC_ERR_MSG(efx, extack, "Failed to lookup neighbour for encap"); - dev_put(neigh->egdev); + netdev_put(neigh->egdev, &neigh->dev_tracker); goto out_free; } refcount_set(&neigh->ref, 1); @@ -273,7 +275,7 @@ static void efx_free_neigh(struct efx_neigh_binder *neigh) rhashtable_remove_fast(&efx->tc->neigh_ht, &neigh->linkage, efx_neigh_ht_params); synchronize_rcu(); - dev_put(neigh->egdev); + netdev_put(neigh->egdev, &neigh->dev_tracker); put_net(neigh->net); kfree(neigh); } diff --git a/src/driver/linux_net/drivers/net/ethernet/sfc/tc_encap_actions.h b/src/driver/linux_net/drivers/net/ethernet/sfc/tc_encap_actions.h index 71d0dc18..af79703e 100644 --- a/src/driver/linux_net/drivers/net/ethernet/sfc/tc_encap_actions.h +++ b/src/driver/linux_net/drivers/net/ethernet/sfc/tc_encap_actions.h @@ -26,6 +26,7 @@ * @ttl: Time To Live associated with the route used * @dying: set when egdev is going away, to skip further updates * @egdev: egress device from the route lookup. Holds a reference + * @dev_tracker: reference tracker entry for @egdev * @ref: counts encap actions referencing this entry * @used: jiffies of last time traffic hit any encap action using this. * When counter reads update this, a new neighbour event is sent to @@ -53,6 +54,7 @@ struct efx_neigh_binder { u8 ttl; bool dying; struct net_device *egdev; + netdevice_tracker dev_tracker; refcount_t ref; unsigned long used; struct list_head users; diff --git a/src/driver/linux_resource/kernel_compat.sh b/src/driver/linux_resource/kernel_compat.sh index d277510b..3167c61b 100755 --- a/src/driver/linux_resource/kernel_compat.sh +++ b/src/driver/linux_resource/kernel_compat.sh @@ -178,6 +178,9 @@ EFRM_HAVE_FOLLOW_PTE_VMA symtype follow_pte include/linux/mm.h int(struct vm_are EFRM_HAVE_LINUX_TPH_H file include/linux/pci-tph.h +EFX_NEED_NETDEV_HOLD nsymbol netdev_hold include/linux/netdevice.h +EFX_HAVE_DEV_HOLD_TRACK symbol dev_hold_track include/linux/netdevice.h + # TODO move onload-related stuff from net kernel_compat " | grep -E -v -e '^#' -e '^$' | sed 's/[ \t][ \t]*/:/g' } diff --git a/src/driver/linux_resource/nondl_resource.c b/src/driver/linux_resource/nondl_resource.c index c7ec99d8..f485f448 100644 --- a/src/driver/linux_resource/nondl_resource.c +++ b/src/driver/linux_resource/nondl_resource.c @@ -8,6 +8,7 @@ #include #include #include +#include /* Maximum number of VIs we will try to create for each device. */ #define MAX_VIS 128 @@ -94,6 +95,9 @@ void efrm_nondl_unregister_driver(struct efrm_nondl_driver *driver) rtnl_lock(); + if (!nondl_driver) + goto out; + EFRM_ASSERT(nondl_driver == driver); list_for_each_entry_safe_reverse(device, device_n, &driver->devices, @@ -104,6 +108,7 @@ void efrm_nondl_unregister_driver(struct efrm_nondl_driver *driver) nondl_driver = NULL; +out: rtnl_unlock(); } EXPORT_SYMBOL(efrm_nondl_unregister_driver); @@ -132,7 +137,7 @@ int efrm_nondl_register_netdev(struct net_device *netdev, INIT_LIST_HEAD(&device->node); - dev_hold(netdev); + netdev_hold(netdev, &device->netdev_tracker, GFP_KERNEL); device->netdev = netdev; device->n_vis = n_vis; device->is_up = 1; @@ -153,7 +158,7 @@ static void efrm_nondl_cleanup_netdev(struct efrm_nondl_device *device) BUG_ON(device->driver); list_del(&device->node); - dev_put(device->netdev); + netdev_put(device->netdev, &device->netdev_tracker); kfree(device); } diff --git a/src/driver/linux_resource/resource_driver.c b/src/driver/linux_resource/resource_driver.c index db1a2ba0..3f17c840 100644 --- a/src/driver/linux_resource/resource_driver.c +++ b/src/driver/linux_resource/resource_driver.c @@ -69,6 +69,7 @@ #include "sfcaffinity.h" #include #include +#include #include "debugfs.h" MODULE_AUTHOR("Solarflare Communications"); @@ -206,7 +207,7 @@ linux_efrm_nic_ctor(struct linux_efhw_nic *lnic, struct device *dev, /* Tie the lifetime of the kernel's state to that of our own. */ if( dev ) get_device(dev); - dev_hold(net_dev); + netdev_hold(net_dev, &nic->net_dev_tracker, GFP_KERNEL); rc = efhw_nic_ctor(nic, res_dim, dev_type, net_dev, dev); if (rc < 0) @@ -236,7 +237,7 @@ linux_efrm_nic_ctor(struct linux_efhw_nic *lnic, struct device *dev, fail1: if( dev ) put_device(dev); - dev_put(net_dev); + netdev_put(net_dev, &nic->net_dev_tracker); return rc; } @@ -259,7 +260,7 @@ linux_efrm_nic_reclaim(struct linux_efhw_nic *lnic, /* Replace the net & pci devs */ get_device(dev); - dev_hold(net_dev); + netdev_hold(net_dev, &nic->net_dev_tracker, GFP_KERNEL); spin_lock_bh(&nic->pci_dev_lock); old_dev = nic->dev; nic->dev = dev; @@ -506,7 +507,7 @@ efrm_nic_do_unplug(struct efhw_nic* nic, bool hard) spin_unlock_bh(&nic->pci_dev_lock); EFRM_ASSERT(net_dev != NULL); - dev_put(net_dev); + netdev_put(net_dev, &nic->net_dev_tracker); put_device(dev); return 0; @@ -631,6 +632,21 @@ static void efrm_nic_del_all(void) efrm_nic_del(linux_efhw_nic(nic)); } +static int sfc_resource_shutdown_notify(struct notifier_block *unused1, + unsigned long unused2, void *unused3) +{ + /* Due to refcounting reasons in netdev, these must + * be called for the shutdown to happen without delays + */ + efrm_nondl_unregister(); + efrm_nondl_shutdown(); + + return NOTIFY_DONE; +} + +static struct notifier_block sfc_resource_shutdown_nb = { + .notifier_call = sfc_resource_shutdown_notify, +}; /**************************************************************************** * @@ -683,6 +699,8 @@ static int init_sfc_resource(void) efrm_install_sysfs_entries(); efrm_nondl_register(); + register_reboot_notifier(&sfc_resource_shutdown_nb); + return 0; failed_notifier: @@ -705,6 +723,8 @@ static int init_sfc_resource(void) ****************************************************************************/ static void cleanup_sfc_resource(void) { + unregister_reboot_notifier(&sfc_resource_shutdown_nb); + efrm_nondl_unregister(); efrm_remove_sysfs_entries(); efrm_nondl_shutdown(); diff --git a/src/include/ci/driver/kernel_compat.h b/src/include/ci/driver/kernel_compat.h index a1d7c91a..3b8e4def 100644 --- a/src/include/ci/driver/kernel_compat.h +++ b/src/include/ci/driver/kernel_compat.h @@ -546,4 +546,30 @@ static inline int efrm_follow_pfn(struct vm_area_struct *vma, } #endif +#ifdef EFX_NEED_NETDEV_HOLD +#ifdef EFX_HAVE_DEV_HOLD_TRACK +/* Commit d62607c3fe45 ("net: rename reference+tracking helpers") + * renamed these. + */ +#define netdev_hold(_n, _t, _g) dev_hold_track(_n, _t, _g) +#define netdev_put(_n, _t) dev_put_track(_n, _t) +#else +/* This was introduced in the same commit that adds dev_hold_track */ +typedef struct {} netdevice_tracker; + +static inline void netdev_hold(struct net_device *dev, + netdevice_tracker *tracker __always_unused, + gfp_t gfp __always_unused) +{ + dev_hold(dev); +} + +static inline void netdev_put(struct net_device *dev, + netdevice_tracker *tracker __always_unused) +{ + dev_put(dev); +} +#endif +#endif + #endif /* DRIVER_LINUX_RESOURCE_KERNEL_COMPAT_H */ diff --git a/src/include/ci/efhw/efhw_types.h b/src/include/ci/efhw/efhw_types.h index d7260ae6..f9cd4095 100644 --- a/src/include/ci/efhw/efhw_types.h +++ b/src/include/ci/efhw/efhw_types.h @@ -40,6 +40,7 @@ #ifndef __CI_EFHW_EFAB_TYPES_H__ #define __CI_EFHW_EFAB_TYPES_H__ +#include #include #include #include @@ -569,6 +570,7 @@ struct efhw_nic { int index; struct net_device *net_dev; /*!< Network device */ + netdevice_tracker net_dev_tracker; struct device *dev; /*!< HW device */ struct pci_dev *pci_dev; /*!< PCI device */ spinlock_t pci_dev_lock; /*!< Protects access to dev & net_dev */ diff --git a/src/include/ci/efrm/nondl.h b/src/include/ci/efrm/nondl.h index f1fd2927..164be270 100644 --- a/src/include/ci/efrm/nondl.h +++ b/src/include/ci/efrm/nondl.h @@ -8,6 +8,7 @@ #define __EFRM_NONDL_H__ #include +#include /* Non-driverlink network device. @@ -43,6 +44,7 @@ struct efrm_nondl_device { /* Network device currently associated with this non-driverlink * device. */ struct net_device *netdev; + netdevice_tracker netdev_tracker; /* Number of VIs we would like to create on this device. */ unsigned int n_vis;