2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-30 22:05:19 +00:00

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>
This commit is contained in:
Ben Pfaff
2013-08-10 20:42:50 -07:00
parent 38e0065b1f
commit 4f9f3f2135

View File

@@ -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;