aboutsummaryrefslogtreecommitdiff
path: root/lib/netdev-linux.c
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2013-08-10 20:42:50 -0700
committerBen Pfaff <blp@nicira.com>2013-08-10 20:42:50 -0700
commit4f9f3f21355bee59abddf6276a0a8bfbe15d2b87 (patch)
tree4b64eada9b17523e02cec344f9d216f743bd2b25 /lib/netdev-linux.c
parent38e0065b1f25615bf69cfdc000d17935f99c022b (diff)
netdev-linux: Avoid deadlock in netdev_linux_update_flags() for taps.
netdev_linux_set_etheraddr() would attempt to recursively acquire netdev->mutex via netdev_linux_update_flags() for tap devices. Reported-by: ZhengLingyun <konghuarukhr@163.com> Tested-by: ZhengLingyun <konghuarukhr@163.com> Signed-off-by: Ben Pfaff <blp@nicira.com>
Diffstat (limited to 'lib/netdev-linux.c')
-rw-r--r--lib/netdev-linux.c35
1 files changed, 26 insertions, 9 deletions
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index d146ccfc..9a80b676 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -410,6 +410,9 @@ static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *,
int cmd, const char *cmd_name);
static int get_flags(const struct netdev *, unsigned int *flags);
static int set_flags(const char *, unsigned int flags);
+static int update_flags(struct netdev_linux *netdev, enum netdev_flags off,
+ enum netdev_flags on, enum netdev_flags *old_flagsp)
+ OVS_REQUIRES(netdev->mutex);
static int do_get_ifindex(const char *netdev_name);
static int get_ifindex(const struct netdev *, int *ifindexp);
static int do_set_addr(struct netdev *netdev,
@@ -979,7 +982,7 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
const uint8_t mac[ETH_ADDR_LEN])
{
struct netdev_linux *netdev = netdev_linux_cast(netdev_);
- struct netdev_saved_flags *sf = NULL;
+ enum netdev_flags old_flags = 0;
int error;
ovs_mutex_lock(&netdev->mutex);
@@ -994,7 +997,7 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
/* Tap devices must be brought down before setting the address. */
if (is_tap_netdev(netdev_)) {
- netdev_turn_flags_off(netdev_, NETDEV_UP, &sf);
+ update_flags(netdev, NETDEV_UP, 0, &old_flags);
}
error = set_etheraddr(netdev_get_name(netdev_), mac);
if (!error || error == ENODEV) {
@@ -1005,7 +1008,9 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
}
}
- netdev_restore_flags(sf);
+ if (is_tap_netdev(netdev_) && old_flags & NETDEV_UP) {
+ update_flags(netdev, 0, NETDEV_UP, &old_flags);
+ }
exit:
ovs_mutex_unlock(&netdev->mutex);
@@ -2478,21 +2483,33 @@ iff_to_nd_flags(int iff)
}
static int
-netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
- enum netdev_flags on, enum netdev_flags *old_flagsp)
+update_flags(struct netdev_linux *netdev, enum netdev_flags off,
+ enum netdev_flags on, enum netdev_flags *old_flagsp)
+ OVS_REQUIRES(netdev->mutex)
{
- struct netdev_linux *netdev = netdev_linux_cast(netdev_);
int old_flags, new_flags;
int error = 0;
- ovs_mutex_lock(&netdev->mutex);
old_flags = netdev->ifi_flags;
*old_flagsp = iff_to_nd_flags(old_flags);
new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
if (new_flags != old_flags) {
- error = set_flags(netdev_get_name(netdev_), new_flags);
- get_flags(netdev_, &netdev->ifi_flags);
+ error = set_flags(netdev_get_name(&netdev->up), new_flags);
+ get_flags(&netdev->up, &netdev->ifi_flags);
}
+
+ return error;
+}
+
+static int
+netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
+ enum netdev_flags on, enum netdev_flags *old_flagsp)
+{
+ struct netdev_linux *netdev = netdev_linux_cast(netdev_);
+ int error;
+
+ ovs_mutex_lock(&netdev->mutex);
+ error = update_flags(netdev, off, on, old_flagsp);
ovs_mutex_unlock(&netdev->mutex);
return error;