History

net: refine dev_put()/dev_hold() debugging

We are still chasing some syzbot reports where we think a rogue dev_put() is called with no corresponding prior dev_hold(). Unfortunately it eats a reference on dev->dev_refcnt taken by innocent dev_hold_track(), meaning that the refcount saturation splat comes too late to be useful. Make sure that 'not tracked' dev_put() and dev_hold() better use CONFIG_NET_DEV_REFCNT_TRACKER=y debug infrastructure: Prior patch in the series allowed ref_tracker_alloc() and ref_tracker_free() to be called with a NULL @trackerp parameter, and to use a separate refcount only to detect too many put() even in the following case: dev_hold_track(dev, tracker_1, GFP_ATOMIC); dev_hold(dev); dev_put(dev); dev_put(dev); // Should complain loudly here. dev_put_track(dev, tracker_1); // instead of here Add clarification about netdev_tracker_alloc() role. v2: I replaced the dev_put() in linkwatch_do_dev() with __dev_put() because callers called netdev_tracker_free().

Eric Dumazet committed 4 years ago

Commit 4c6c11e1 parent 8fd55223 files · +50 −27Browse files

Showing the changes to include (1 of 3 files). Show all

Unified · Split

include/linux/netdevice.h+46−23
@@ -3817,14 +3817,7 @@ extern unsigned int netdev_budget_usecs; · 3816 unmodified lines
38173817 /* Called by rtnetlink.c:rtnl_unlock() */
38183818 void netdev_run_todo(void);
38193819
3820−/**
3821− * dev_put - release reference to device
3822− * @dev: network device
3823− *
3824− * Release reference to device to allow it to be freed.
3825− * Try using dev_put_track() instead.
3826− */
3827−static inline void dev_put(struct net_device *dev)
3820+static inline void __dev_put(struct net_device *dev)
38283821 {
38293822 if (dev) {
38303823 #ifdef CONFIG_PCPU_DEV_REFCNT
@@ -3835,14 +3828,7 @@ static inline void dev_put(struct net_device *dev) · 4 unmodified lines
38353828 }
38363829 }
38373830
3838−/**
3839− * dev_hold - get reference to device
3840− * @dev: network device
3841− *
3842− * Hold reference to device to keep it from being freed.
3843− * Try using dev_hold_track() instead.
3844− */
3845−static inline void dev_hold(struct net_device *dev)
3831+static inline void __dev_hold(struct net_device *dev)
38463832 {
38473833 if (dev) {
38483834 #ifdef CONFIG_PCPU_DEV_REFCNT
@@ -3853,11 +3839,24 @@ static inline void dev_hold(struct net_device *dev) · 4 unmodified lines
38533839 }
38543840 }
38553841
3842+static inline void __netdev_tracker_alloc(struct net_device *dev,
3843+ netdevice_tracker *tracker,
3844+ gfp_t gfp)
3845+{
3846+#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
3847+ ref_tracker_alloc(&dev->refcnt_tracker, tracker, gfp);
3848+#endif
3849+}
3850+
3851+/* netdev_tracker_alloc() can upgrade a prior untracked reference
3852+ * taken by dev_get_by_name()/dev_get_by_index() to a tracked one.
3853+ */
38563854 static inline void netdev_tracker_alloc(struct net_device *dev,
38573855 netdevice_tracker *tracker, gfp_t gfp)
38583856 {
38593857 #ifdef CONFIG_NET_DEV_REFCNT_TRACKER
3860− ref_tracker_alloc(&dev->refcnt_tracker, tracker, gfp);
3858+ refcount_dec(&dev->refcnt_tracker.no_tracker);
3859+ __netdev_tracker_alloc(dev, tracker, gfp);
38613860 #endif
38623861 }
38633862
@@ -3873,8 +3872,8 @@ static inline void dev_hold_track(struct net_device *dev, · 9 unmodified lines
38733872 netdevice_tracker *tracker, gfp_t gfp)
38743873 {
38753874 if (dev) {
3876− dev_hold(dev);
3877− netdev_tracker_alloc(dev, tracker, gfp);
3875+ __dev_hold(dev);
3876+ __netdev_tracker_alloc(dev, tracker, gfp);
38783877 }
38793878 }
38803879
@@ -3883,10 +3882,34 @@ static inline void dev_put_track(struct net_device *dev, · 2 unmodified lines
38833882 {
38843883 if (dev) {
38853884 netdev_tracker_free(dev, tracker);
3886− dev_put(dev);
3885+ __dev_put(dev);
38873886 }
38883887 }
38893888
3889+/**
3890+ * dev_hold - get reference to device
3891+ * @dev: network device
3892+ *
3893+ * Hold reference to device to keep it from being freed.
3894+ * Try using dev_hold_track() instead.
3895+ */
3896+static inline void dev_hold(struct net_device *dev)
3897+{
3898+ dev_hold_track(dev, NULL, GFP_ATOMIC);
3899+}
3900+
3901+/**
3902+ * dev_put - release reference to device
3903+ * @dev: network device
3904+ *
3905+ * Release reference to device to allow it to be freed.
3906+ * Try using dev_put_track() instead.
3907+ */
3908+static inline void dev_put(struct net_device *dev)
3909+{
3910+ dev_put_track(dev, NULL);
3911+}
3912+
38903913 static inline void dev_replace_track(struct net_device *odev,
38913914 struct net_device *ndev,
38923915 netdevice_tracker *tracker,
@@ -3895,11 +3918,11 @@ static inline void dev_replace_track(struct net_device *odev, · 2 unmodified lines
38953918 if (odev)
38963919 netdev_tracker_free(odev, tracker);
38973920
3898− dev_hold(ndev);
3899− dev_put(odev);
3921+ __dev_hold(ndev);
3922+ __dev_put(odev);
39003923
39013924 if (ndev)
3902− netdev_tracker_alloc(ndev, tracker, gfp);
3925+ __netdev_tracker_alloc(ndev, tracker, gfp);
39033926 }
39043927
39053928 /* Carrier loss detection, dial on demand. The functions netif_carrier_on

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t