Protect concurrent access when calling netdev_get_features() or netdev_get_speed() for BSD netdevs.
Signed-off-by: David Marchand <david.march...@redhat.com> --- lib/netdev-bsd.c | 111 +++++++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 43 deletions(-) diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c index c7cd59376a..7718c30a04 100644 --- a/lib/netdev-bsd.c +++ b/lib/netdev-bsd.c @@ -92,6 +92,11 @@ struct netdev_bsd { struct eth_addr etheraddr; int mtu; int carrier; + int get_features_error; + + enum netdev_features current; + enum netdev_features advertised; + enum netdev_features supported; int tap_fd; /* TAP character device, if any, otherwise -1. */ @@ -1099,28 +1104,22 @@ netdev_bsd_parse_media(int media) return supported; } -/* - * Stores the features supported by 'netdev' into each of '*current', - * '*advertised', '*supported', and '*peer' that are non-null. Each value is a - * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if - * successful, otherwise a positive errno value. On failure, all of the - * passed-in values are set to 0. - */ -static int -netdev_bsd_get_features(const struct netdev *netdev, - enum netdev_features *current, uint32_t *advertised, - enum netdev_features *supported, uint32_t *peer) +static void +netdev_bsd_read_features(struct netdev_bsd *netdev) { struct ifmediareq ifmr; int *media_list; - int i; int error; - + int i; /* XXX Look into SIOCGIFCAP instead of SIOCGIFMEDIA */ memset(&ifmr, 0, sizeof(ifmr)); - ovs_strlcpy(ifmr.ifm_name, netdev_get_name(netdev), sizeof ifmr.ifm_name); + ovs_strlcpy(ifmr.ifm_name, netdev_get_name(&netdev->up), + sizeof ifmr.ifm_name); + + media_list = xcalloc(ifmr.ifm_count, sizeof(int)); + ifmr.ifm_ulist = media_list; /* We make two SIOCGIFMEDIA ioctl calls. The first to determine the * number of supported modes, and a second with a buffer to retrieve @@ -1128,16 +1127,13 @@ netdev_bsd_get_features(const struct netdev *netdev, error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr); if (error) { VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s", - netdev_get_name(netdev), ovs_strerror(error)); - return error; + netdev_get_name(&netdev->up), ovs_strerror(error)); + goto cleanup; } - media_list = xcalloc(ifmr.ifm_count, sizeof(int)); - ifmr.ifm_ulist = media_list; - if (IFM_TYPE(ifmr.ifm_current) != IFM_ETHER) { VLOG_DBG_RL(&rl, "%s: doesn't appear to be ethernet", - netdev_get_name(netdev)); + netdev_get_name(&netdev->up)); error = EINVAL; goto cleanup; } @@ -1145,53 +1141,82 @@ netdev_bsd_get_features(const struct netdev *netdev, error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr); if (error) { VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s", - netdev_get_name(netdev), ovs_strerror(error)); + netdev_get_name(&netdev->up), ovs_strerror(error)); goto cleanup; } /* Current settings. */ - *current = netdev_bsd_parse_media(ifmr.ifm_active); - if (!*current) { - *current = NETDEV_F_OTHER; + netdev->current = netdev_bsd_parse_media(ifmr.ifm_active); + if (!netdev->current) { + netdev->current = NETDEV_F_OTHER; } /* Advertised features. */ - *advertised = netdev_bsd_parse_media(ifmr.ifm_current); + netdev->advertised = netdev_bsd_parse_media(ifmr.ifm_current); /* Supported features. */ - *supported = 0; + netdev->supported = 0; for (i = 0; i < ifmr.ifm_count; i++) { - *supported |= netdev_bsd_parse_media(ifmr.ifm_ulist[i]); + netdev->supported |= netdev_bsd_parse_media(ifmr.ifm_ulist[i]); } - /* Peer advertisements. */ - *peer = 0; /* XXX */ - error = 0; + cleanup: free(media_list); + netdev->get_features_error = error; +} + +/* + * Stores the features supported by 'netdev' into each of '*current', + * '*advertised', '*supported', and '*peer' that are non-null. Each value is a + * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if + * successful, otherwise a positive errno value. On failure, all of the + * passed-in values are set to 0. + */ +static int +netdev_bsd_get_features(const struct netdev *netdev_, + enum netdev_features *current, uint32_t *advertised, + enum netdev_features *supported, uint32_t *peer) +{ + struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); + int error; + + ovs_mutex_lock(&netdev->mutex); + netdev_bsd_read_features(netdev); + if (!netdev->get_features_error) { + *current = netdev->current; + *advertised = netdev->advertised; + *supported = netdev->supported; + *peer = 0; + } + error = netdev->get_features_error; + ovs_mutex_unlock(&netdev->mutex); + return error; } static int -netdev_bsd_get_speed(const struct netdev *netdev, uint32_t *current, +netdev_bsd_get_speed(const struct netdev *netdev_, uint32_t *current, uint32_t *max) { - enum netdev_features f_current, f_supported, f_advertised, f_peer; + struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int error; - error = netdev_bsd_get_features(netdev, &f_current, &f_advertised, - &f_supported, &f_peer); - if (error) { - return error; - } - - *current = MIN(UINT32_MAX, - netdev_features_to_bps(f_current, 0) / 1000000ULL); - *max = MIN(UINT32_MAX, - netdev_features_to_bps(f_supported, 0) / 1000000ULL); + ovs_mutex_lock(&netdev->mutex); + netdev_bsd_read_features(netdev); + if (!netdev->get_features_error) { + *current = MIN(UINT32_MAX, + netdev_features_to_bps(netdev->current, 0) + / 1000000ULL); + *max = MIN(UINT32_MAX, + netdev_features_to_bps(netdev->supported, 0) + / 1000000ULL); + } + error = netdev->get_features_error; + ovs_mutex_unlock(&netdev->mutex); - return 0; + return error; } /* -- 2.50.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev