From 4e335f640fadda81aff9dd2dc06703961bea2826 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 4 Sep 2011 15:00:25 +0200 Subject: [PATCH] make device_claim/device_release operate on the device_user instead of the device to avoid refcount bugs --- bridge.c | 4 ++-- device.c | 18 ++++++++++++++++-- device.h | 5 +++-- interface.c | 4 ++-- vlan.c | 6 +++--- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/bridge.c b/bridge.c index 3f8739c..c48381b 100644 --- a/bridge.c +++ b/bridge.c @@ -72,7 +72,7 @@ bridge_disable_member(struct bridge_member *bm) return 0; system_bridge_delif(&bst->dev, bm->dev.dev); - device_release(bm->dev.dev); + device_release(&bm->dev); return 0; } @@ -86,7 +86,7 @@ bridge_enable_member(struct bridge_member *bm) if (!bm->present) return 0; - ret = device_claim(bm->dev.dev); + ret = device_claim(&bm->dev); if (ret < 0) goto error; diff --git a/device.c b/device.c index 0019b6c..c02335c 100644 --- a/device.c +++ b/device.c @@ -120,10 +120,15 @@ static int set_device_state(struct device *dev, bool state) return 0; } -int device_claim(struct device *dev) +int device_claim(struct device_user *dep) { + struct device *dev = dep->dev; int ret; + if (dep->claimed) + return 0; + + dep->claimed = true; DPRINTF("claim device %s, new refcount: %d\n", dev->ifname, dev->active + 1); if (++dev->active != 1) return 0; @@ -138,8 +143,14 @@ int device_claim(struct device *dev) return ret; } -void device_release(struct device *dev) +void device_release(struct device_user *dep) { + struct device *dev = dep->dev; + + if (!dep->claimed) + return; + + dep->claimed = false; dev->active--; DPRINTF("release device %s, new refcount: %d\n", dev->ifname, dev->active); assert(dev->active >= 0); @@ -254,6 +265,9 @@ void device_remove_user(struct device_user *dep) { struct device *dev = dep->dev; + if (dep->claimed) + device_release(dep); + list_del(&dep->list); if (list_empty(&dev->users)) { diff --git a/device.h b/device.h index e11df1f..63ffb21 100644 --- a/device.h +++ b/device.h @@ -124,6 +124,7 @@ enum device_event { struct device_user { struct list_head list; + bool claimed; struct device *dev; void (*cb)(struct device_user *, enum device_event); }; @@ -147,8 +148,8 @@ void device_add_user(struct device_user *dep, struct device *iface); void device_remove_user(struct device_user *dep); void device_set_present(struct device *dev, bool state); -int device_claim(struct device *dev); -void device_release(struct device *dev); +int device_claim(struct device_user *dep); +void device_release(struct device_user *dep); int check_device_state(struct device *dev); static inline void diff --git a/interface.c b/interface.c index f08fee0..f6c32f4 100644 --- a/interface.c +++ b/interface.c @@ -89,7 +89,7 @@ static void mark_interface_down(struct interface *iface) { interface_del_ctx_addr(iface, NULL); - device_release(iface->main_dev.dev); + device_release(&iface->main_dev); iface->state = IFS_DOWN; } @@ -101,7 +101,7 @@ __interface_set_up(struct interface *iface) if (iface->state != IFS_DOWN) return 0; - ret = device_claim(iface->main_dev.dev); + ret = device_claim(&iface->main_dev); if (ret) return ret; diff --git a/vlan.c b/vlan.c index 18cb939..d00cda0 100644 --- a/vlan.c +++ b/vlan.c @@ -32,18 +32,18 @@ static int vlan_set_device_state(struct device *dev, bool up) if (!up) { vldev->set_state(dev, false); system_vlan_del(dev); - device_release(vldev->dep.dev); + device_release(&vldev->dep); return 0; } - ret = device_claim(vldev->dep.dev); + ret = device_claim(&vldev->dep); if (ret) return ret; system_vlan_add(vldev->dep.dev, vldev->id); ret = vldev->set_state(dev, true); if (ret) - device_release(vldev->dep.dev); + device_release(&vldev->dep); return ret; } -- 2.25.1