X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=cf459059b40f20a9b62c8801561b5e97019427f6;hb=b0fb94a346e52f36aeef238dd5f9bef9a10c14ef;hp=05877c1a4de457b2284ef8970f54d3dbddad8096;hpb=a88b4e04128310d0eb0c3d811782f8619bceb95c;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 05877c1a4..cf459059b 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -120,10 +120,6 @@ enum { VALID_DRVINFO = 1 << 7, VALID_FEATURES = 1 << 8, }; - -struct tap_state { - int fd; -}; /* Traffic control. */ @@ -148,6 +144,7 @@ struct tc { struct tc_queue { struct hmap_node hmap_node; /* In struct tc's "queues" hmap. */ unsigned int queue_id; /* OpenFlow queue ID. */ + long long int created; /* Time queue was created, in msecs. */ }; /* A particular kind of traffic control. Each implementation generally maps to @@ -358,7 +355,6 @@ static int tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes); struct netdev_linux { struct netdev up; - struct shash_node *shash_node; unsigned int cache_valid; unsigned int change_seq; @@ -388,14 +384,12 @@ struct netdev_linux { enum netdev_features current; /* Cached from ETHTOOL_GSET. */ enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */ enum netdev_features supported; /* Cached from ETHTOOL_GSET. */ - enum netdev_features peer; /* Cached from ETHTOOL_GSET. */ struct ethtool_drvinfo drvinfo; /* Cached from ETHTOOL_GDRVINFO. */ struct tc *tc; - union { - struct tap_state tap; - } state; + /* For devices of class netdev_tap_class only. */ + int tap_fd; }; struct netdev_rx_linux { @@ -404,21 +398,14 @@ struct netdev_rx_linux { int fd; }; -static const struct netdev_rx_class netdev_rx_linux_class; - -/* Sockets used for ioctl operations. */ -static int af_inet_sock = -1; /* AF_INET, SOCK_DGRAM. */ - /* This is set pretty low because we probably won't learn anything from the * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); -static int netdev_linux_init(void); +static void netdev_linux_run(void); static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *, int cmd, const char *cmd_name); -static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd, - const char *cmd_name); 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); @@ -439,7 +426,7 @@ static void netdev_linux_miimon_wait(void); static bool is_netdev_linux_class(const struct netdev_class *netdev_class) { - return netdev_class->init == netdev_linux_init; + return netdev_class->run == netdev_linux_run; } static bool @@ -459,25 +446,10 @@ netdev_linux_cast(const struct netdev *netdev) static struct netdev_rx_linux * netdev_rx_linux_cast(const struct netdev_rx *rx) { - netdev_rx_assert_class(rx, &netdev_rx_linux_class); + ovs_assert(is_netdev_linux_class(netdev_get_class(rx->netdev))); return CONTAINER_OF(rx, struct netdev_rx_linux, up); } -static int -netdev_linux_init(void) -{ - static int status = -1; - if (status < 0) { - /* Create AF_INET socket. */ - af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0); - status = af_inet_sock >= 0 ? 0 : errno; - if (status) { - VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status)); - } - } - return status; -} - static void netdev_linux_run(void) { @@ -543,11 +515,11 @@ static void netdev_linux_cache_cb(const struct rtnetlink_link_change *change, void *aux OVS_UNUSED) { - struct netdev_linux *dev; if (change) { struct netdev *base_dev = netdev_from_name(change->ifname); if (base_dev && is_netdev_linux_class(netdev_get_class(base_dev))) { netdev_linux_update(netdev_linux_cast(base_dev), change); + netdev_close(base_dev); } } else { struct shash device_shash; @@ -556,12 +528,13 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, shash_init(&device_shash); netdev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { + struct netdev *netdev = node->data; + struct netdev_linux *dev = netdev_linux_cast(netdev); unsigned int flags; - dev = node->data; - get_flags(&dev->up, &flags); netdev_linux_changed(dev, flags, 0); + netdev_close(netdev); } shash_destroy(&device_shash); } @@ -596,29 +569,38 @@ cache_notifier_unref(void) } } +static struct netdev * +netdev_linux_alloc(void) +{ + struct netdev_linux *netdev = xzalloc(sizeof *netdev); + return &netdev->up; +} + +static int +netdev_linux_common_construct(struct netdev_linux *netdev) +{ + netdev->change_seq = 1; + + return cache_notifier_ref(); +} + /* Creates system and internal devices. */ static int -netdev_linux_create(const struct netdev_class *class, const char *name, - struct netdev **netdevp) +netdev_linux_construct(struct netdev *netdev_) { - struct netdev_linux *netdev; + struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; - error = cache_notifier_ref(); + error = netdev_linux_common_construct(netdev); if (error) { return error; } - netdev = xzalloc(sizeof *netdev); - netdev->change_seq = 1; - netdev_init(&netdev->up, name, class); error = get_flags(&netdev->up, &netdev->ifi_flags); if (error == ENODEV) { - if (class != &netdev_internal_class) { + if (netdev->up.netdev_class != &netdev_internal_class) { /* The device does not exist, so don't allow it to be opened. */ - netdev_uninit(&netdev->up, false); cache_notifier_unref(); - free(netdev); return ENODEV; } else { /* "Internal" netdevs have to be created as netdev objects before @@ -628,7 +610,6 @@ netdev_linux_create(const struct netdev_class *class, const char *name, } } - *netdevp = &netdev->up; return 0; } @@ -639,26 +620,22 @@ netdev_linux_create(const struct netdev_class *class, const char *name, * buffers, across all readers. Therefore once data is read it will * be unavailable to other reads for tap devices. */ static int -netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, - const char *name, struct netdev **netdevp) +netdev_linux_construct_tap(struct netdev *netdev_) { - struct netdev_linux *netdev; - struct tap_state *state; + struct netdev_linux *netdev = netdev_linux_cast(netdev_); static const char tap_dev[] = "/dev/net/tun"; + const char *name = netdev_->name; struct ifreq ifr; int error; - netdev = xzalloc(sizeof *netdev); - state = &netdev->state.tap; - - error = cache_notifier_ref(); + error = netdev_linux_common_construct(netdev); if (error) { goto error; } /* Open tap device. */ - state->fd = open(tap_dev, O_RDWR); - if (state->fd < 0) { + netdev->tap_fd = open(tap_dev, O_RDWR); + if (netdev->tap_fd < 0) { error = errno; VLOG_WARN("opening \"%s\" failed: %s", tap_dev, ovs_strerror(error)); goto error_unref_notifier; @@ -667,43 +644,31 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, /* Create tap device. */ ifr.ifr_flags = IFF_TAP | IFF_NO_PI; ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); - if (ioctl(state->fd, TUNSETIFF, &ifr) == -1) { + if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) { VLOG_WARN("%s: creating tap device failed: %s", name, ovs_strerror(errno)); error = errno; - goto error_unref_notifier; + goto error_close; } /* Make non-blocking. */ - error = set_nonblocking(state->fd); + error = set_nonblocking(netdev->tap_fd); if (error) { - goto error_unref_notifier; + goto error_close; } - netdev_init(&netdev->up, name, &netdev_tap_class); - *netdevp = &netdev->up; return 0; +error_close: + close(netdev->tap_fd); error_unref_notifier: cache_notifier_unref(); error: - free(netdev); return error; } static void -destroy_tap(struct netdev_linux *netdev) -{ - struct tap_state *state = &netdev->state.tap; - - if (state->fd >= 0) { - close(state->fd); - } -} - -/* Destroys the netdev device 'netdev_'. */ -static void -netdev_linux_destroy(struct netdev *netdev_) +netdev_linux_destruct(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); @@ -711,47 +676,64 @@ netdev_linux_destroy(struct netdev *netdev_) netdev->tc->ops->tc_destroy(netdev->tc); } - if (netdev_get_class(netdev_) == &netdev_tap_class) { - destroy_tap(netdev); + if (netdev_get_class(netdev_) == &netdev_tap_class + && netdev->tap_fd >= 0) + { + close(netdev->tap_fd); } - free(netdev); cache_notifier_unref(); } +static void +netdev_linux_dealloc(struct netdev *netdev_) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + free(netdev); +} + +static struct netdev_rx * +netdev_linux_rx_alloc(void) +{ + struct netdev_rx_linux *rx = xzalloc(sizeof *rx); + return &rx->up; +} + static int -netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp) +netdev_linux_rx_construct(struct netdev_rx *rx_) { + struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); + struct netdev *netdev_ = rx->up.netdev; struct netdev_linux *netdev = netdev_linux_cast(netdev_); - bool is_tap = is_tap_netdev(netdev_); - struct netdev_rx_linux *rx; int error; - int fd; - if (is_tap) { - fd = netdev->state.tap.fd; + rx->is_tap = is_tap_netdev(netdev_); + if (rx->is_tap) { + rx->fd = netdev->tap_fd; } else { struct sockaddr_ll sll; int ifindex; /* Result of tcpdump -dd inbound */ - static struct sock_filter filt[] = { + static const struct sock_filter filt[] = { { 0x28, 0, 0, 0xfffff004 }, /* ldh [0] */ { 0x15, 0, 1, 0x00000004 }, /* jeq #4 jt 2 jf 3 */ { 0x6, 0, 0, 0x00000000 }, /* ret #0 */ { 0x6, 0, 0, 0x0000ffff } /* ret #65535 */ }; - static struct sock_fprog fprog = { ARRAY_SIZE(filt), filt }; + static const struct sock_fprog fprog = { + ARRAY_SIZE(filt), (struct sock_filter *) filt + }; /* Create file descriptor. */ - fd = socket(PF_PACKET, SOCK_RAW, 0); - if (fd < 0) { + rx->fd = socket(PF_PACKET, SOCK_RAW, 0); + if (rx->fd < 0) { error = errno; VLOG_ERR("failed to create raw socket (%s)", ovs_strerror(error)); goto error; } /* Set non-blocking mode. */ - error = set_nonblocking(fd); + error = set_nonblocking(rx->fd); if (error) { goto error; } @@ -767,7 +749,7 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp) sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; sll.sll_protocol = (OVS_FORCE unsigned short int) htons(ETH_P_ALL); - if (bind(fd, (struct sockaddr *) &sll, sizeof sll) < 0) { + if (bind(rx->fd, (struct sockaddr *) &sll, sizeof sll) < 0) { error = errno; VLOG_ERR("%s: failed to bind raw socket (%s)", netdev_get_name(netdev_), ovs_strerror(error)); @@ -775,44 +757,45 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp) } /* Filter for only inbound packets. */ - error = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, + error = setsockopt(rx->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof fprog); if (error) { error = errno; - VLOG_ERR("%s: failed attach filter (%s)", + VLOG_ERR("%s: failed to attach filter (%s)", netdev_get_name(netdev_), ovs_strerror(error)); goto error; } } - rx = xmalloc(sizeof *rx); - netdev_rx_init(&rx->up, netdev_, &netdev_rx_linux_class); - rx->is_tap = is_tap; - rx->fd = fd; - - *rxp = &rx->up; return 0; error: - if (fd >= 0) { - close(fd); + if (rx->fd >= 0) { + close(rx->fd); } return error; } static void -netdev_rx_linux_destroy(struct netdev_rx *rx_) +netdev_linux_rx_destruct(struct netdev_rx *rx_) { struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); if (!rx->is_tap) { close(rx->fd); } +} + +static void +netdev_linux_rx_dealloc(struct netdev_rx *rx_) +{ + struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); + free(rx); } static int -netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size) +netdev_linux_rx_recv(struct netdev_rx *rx_, void *data, size_t size) { struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); ssize_t retval; @@ -835,20 +818,20 @@ netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size) } static void -netdev_rx_linux_wait(struct netdev_rx *rx_) +netdev_linux_rx_wait(struct netdev_rx *rx_) { struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); poll_fd_wait(rx->fd, POLLIN); } static int -netdev_rx_linux_drain(struct netdev_rx *rx_) +netdev_linux_rx_drain(struct netdev_rx *rx_) { struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); if (rx->is_tap) { struct ifreq ifr; - int error = netdev_linux_do_ioctl(netdev_rx_get_name(rx_), &ifr, - SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); + int error = af_inet_ifreq_ioctl(netdev_rx_get_name(rx_), &ifr, + SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); if (error) { return error; } @@ -919,7 +902,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) * because we attach a socket filter to the rx socket. */ struct netdev_linux *netdev = netdev_linux_cast(netdev_); - retval = write(netdev->state.tap.fd, data, size); + retval = write(netdev->tap_fd, data, size); } if (retval < 0) { @@ -1032,8 +1015,8 @@ netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup) struct ifreq ifr; int error; - error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, - SIOCGIFMTU, "SIOCGIFMTU"); + error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr, + SIOCGIFMTU, "SIOCGIFMTU"); netdev->netdev_mtu_error = error; netdev->mtu = ifr.ifr_mtu; @@ -1066,8 +1049,8 @@ netdev_linux_set_mtu(const struct netdev *netdev_, int mtu) netdev->cache_valid &= ~VALID_MTU; } ifr.ifr_mtu = mtu; - error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, - SIOCSIFMTU, "SIOCSIFMTU"); + error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr, + SIOCSIFMTU, "SIOCSIFMTU"); if (!error || error == ENODEV) { netdev->netdev_mtu_error = error; netdev->mtu = ifr.ifr_mtu; @@ -1116,7 +1099,7 @@ netdev_linux_do_miimon(const char *name, int cmd, const char *cmd_name, memset(&ifr, 0, sizeof ifr); memcpy(&ifr.ifr_data, data, sizeof *data); - error = netdev_linux_do_ioctl(name, &ifr, cmd, cmd_name); + error = af_inet_ifreq_ioctl(name, &ifr, cmd, cmd_name); memcpy(data, &ifr.ifr_data, sizeof *data); return error; @@ -1190,10 +1173,12 @@ netdev_linux_miimon_run(void) shash_init(&device_shash); netdev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { - struct netdev_linux *dev = node->data; + struct netdev *netdev = node->data; + struct netdev_linux *dev = netdev_linux_cast(netdev); bool miimon; if (dev->miimon_interval <= 0 || !timer_expired(&dev->miimon_timer)) { + netdev_close(netdev); continue; } @@ -1204,6 +1189,7 @@ netdev_linux_miimon_run(void) } timer_set_duration(&dev->miimon_timer, dev->miimon_interval); + netdev_close(netdev); } shash_destroy(&device_shash); @@ -1218,11 +1204,13 @@ netdev_linux_miimon_wait(void) shash_init(&device_shash); netdev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { - struct netdev_linux *dev = node->data; + struct netdev *netdev = node->data; + struct netdev_linux *dev = netdev_linux_cast(netdev); if (dev->miimon_interval > 0) { timer_wait(&dev->miimon_timer); } + netdev_close(netdev); } shash_destroy(&device_shash); } @@ -1340,11 +1328,13 @@ static int netdev_linux_sys_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { - static int use_netlink_stats = -1; + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + static int use_netlink_stats; int error; - if (use_netlink_stats < 0) { + if (ovsthread_once_start(&once)) { use_netlink_stats = check_for_working_netlink_stats(); + ovsthread_once_done(&once); } if (use_netlink_stats) { @@ -1415,8 +1405,7 @@ netdev_linux_get_stats(const struct netdev *netdev_, /* Retrieves current device stats for 'netdev-tap' netdev or * netdev-internal. */ static int -netdev_tap_get_stats(const struct netdev *netdev_, - struct netdev_stats *stats) +netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct netdev_stats dev_stats; @@ -1641,18 +1630,14 @@ netdev_linux_read_features(struct netdev_linux *netdev) netdev->current |= NETDEV_F_AUTONEG; } - /* Peer advertisements. */ - netdev->peer = 0; /* XXX */ - out: netdev->cache_valid |= VALID_FEATURES; 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 NETDEV_* bits. Returns 0 if successful, otherwise a positive - * errno value. */ +/* Stores the features supported by 'netdev' into of '*current', '*advertised', + * '*supported', and '*peer'. Each value is a bitmap of NETDEV_* bits. + * Returns 0 if successful, otherwise a positive errno value. */ static int netdev_linux_get_features(const struct netdev *netdev_, enum netdev_features *current, @@ -1668,7 +1653,7 @@ netdev_linux_get_features(const struct netdev *netdev_, *current = netdev->current; *advertised = netdev->advertised; *supported = netdev->supported; - *peer = netdev->peer; + *peer = 0; /* XXX */ } return netdev->get_features_error; } @@ -1996,9 +1981,11 @@ netdev_linux_get_queue_stats(const struct netdev *netdev_, return EOPNOTSUPP; } else { const struct tc_queue *queue = tc_find_queue(netdev_, queue_id); - return (queue - ? netdev->tc->ops->class_get_stats(netdev_, queue, stats) - : ENOENT); + if (!queue) { + return ENOENT; + } + stats->created = queue->created; + return netdev->tc->ops->class_get_stats(netdev_, queue, stats); } } @@ -2200,11 +2187,10 @@ do_set_addr(struct netdev *netdev, int ioctl_nr, const char *ioctl_name, struct in_addr addr) { struct ifreq ifr; - ovs_strzcpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name); - make_in4_sockaddr(&ifr.ifr_addr, addr); - return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr, - ioctl_name); + make_in4_sockaddr(&ifr.ifr_addr, addr); + return af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr, + ioctl_name); } /* Adds 'router' as a default IP gateway. */ @@ -2220,7 +2206,7 @@ netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router) make_in4_sockaddr(&rt.rt_gateway, router); make_in4_sockaddr(&rt.rt_genmask, any); rt.rt_flags = RTF_UP | RTF_GATEWAY; - error = ioctl(af_inet_sock, SIOCADDRT, &rt) < 0 ? errno : 0; + error = af_inet_ioctl(SIOCADDRT, &rt); if (error) { VLOG_WARN("ioctl(SIOCADDRT): %s", ovs_strerror(error)); } @@ -2346,7 +2332,7 @@ netdev_linux_arp_lookup(const struct netdev *netdev, r.arp_flags = 0; ovs_strzcpy(r.arp_dev, netdev_get_name(netdev), sizeof r.arp_dev); COVERAGE_INC(netdev_arp_lookup); - retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0; + retval = af_inet_ioctl(SIOCGARP, &r); if (!retval) { memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN); } else if (retval != ENXIO) { @@ -2407,23 +2393,23 @@ netdev_linux_change_seq(const struct netdev *netdev) return netdev_linux_cast(netdev)->change_seq; } -#define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS, \ +#define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS, SET_STATS, \ GET_FEATURES, GET_STATUS) \ { \ NAME, \ \ - netdev_linux_init, \ + NULL, \ netdev_linux_run, \ netdev_linux_wait, \ \ - CREATE, \ - netdev_linux_destroy, \ + netdev_linux_alloc, \ + CONSTRUCT, \ + netdev_linux_destruct, \ + netdev_linux_dealloc, \ NULL, /* get_config */ \ NULL, /* set_config */ \ NULL, /* get_tunnel_config */ \ \ - netdev_linux_rx_open, \ - \ netdev_linux_send, \ netdev_linux_send_wait, \ \ @@ -2463,13 +2449,21 @@ netdev_linux_change_seq(const struct netdev *netdev) \ netdev_linux_update_flags, \ \ - netdev_linux_change_seq \ + netdev_linux_change_seq, \ + \ + netdev_linux_rx_alloc, \ + netdev_linux_rx_construct, \ + netdev_linux_rx_destruct, \ + netdev_linux_rx_dealloc, \ + netdev_linux_rx_recv, \ + netdev_linux_rx_wait, \ + netdev_linux_rx_drain, \ } const struct netdev_class netdev_linux_class = NETDEV_LINUX_CLASS( "system", - netdev_linux_create, + netdev_linux_construct, netdev_linux_get_stats, NULL, /* set_stats */ netdev_linux_get_features, @@ -2478,7 +2472,7 @@ const struct netdev_class netdev_linux_class = const struct netdev_class netdev_tap_class = NETDEV_LINUX_CLASS( "tap", - netdev_linux_create_tap, + netdev_linux_construct_tap, netdev_tap_get_stats, NULL, /* set_stats */ netdev_linux_get_features, @@ -2487,18 +2481,11 @@ const struct netdev_class netdev_tap_class = const struct netdev_class netdev_internal_class = NETDEV_LINUX_CLASS( "internal", - netdev_linux_create, + netdev_linux_construct, netdev_internal_get_stats, netdev_internal_set_stats, NULL, /* get_features */ netdev_internal_get_status); - -static const struct netdev_rx_class netdev_rx_linux_class = { - netdev_rx_linux_destroy, - netdev_rx_linux_recv, - netdev_rx_linux_wait, - netdev_rx_linux_drain, -}; /* HTB traffic control class. */ @@ -2805,6 +2792,7 @@ htb_update_queue__(struct netdev *netdev, unsigned int queue_id, hcp = xmalloc(sizeof *hcp); queue = &hcp->tc_queue; queue->queue_id = queue_id; + queue->created = time_msec(); hmap_insert(&htb->tc.queues, &queue->hmap_node, hash); } @@ -3038,6 +3026,7 @@ hfsc_update_queue__(struct netdev *netdev, unsigned int queue_id, hcp = xmalloc(sizeof *hcp); queue = &hcp->tc_queue; queue->queue_id = queue_id; + queue->created = time_msec(); hmap_insert(&hfsc->tc.queues, &queue->hmap_node, hash); } @@ -3777,30 +3766,35 @@ read_psched(void) * [5] 2.6.32.21.22 (approx.) from Ubuntu 10.04 on VMware Fusion * [6] 2.6.34 from kernel.org on KVM */ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static const char fn[] = "/proc/net/psched"; unsigned int a, b, c, d; FILE *stream; + if (!ovsthread_once_start(&once)) { + return; + } + ticks_per_s = 1.0; buffer_hz = 100; stream = fopen(fn, "r"); if (!stream) { VLOG_WARN("%s: open failed: %s", fn, ovs_strerror(errno)); - return; + goto exit; } if (fscanf(stream, "%x %x %x %x", &a, &b, &c, &d) != 4) { VLOG_WARN("%s: read failed", fn); fclose(stream); - return; + goto exit; } VLOG_DBG("%s: psched parameters are: %u %u %u %u", fn, a, b, c, d); fclose(stream); if (!a || !c) { VLOG_WARN("%s: invalid scheduler parameters", fn); - return; + goto exit; } ticks_per_s = (double) a * c / b; @@ -3811,6 +3805,9 @@ read_psched(void) fn, a, b, c, d); } VLOG_DBG("%s: ticks_per_s=%f buffer_hz=%u", fn, ticks_per_s, buffer_hz); + +exit: + ovsthread_once_done(&once); } /* Returns the number of bytes that can be transmitted in 'ticks' ticks at a @@ -3818,9 +3815,7 @@ read_psched(void) static unsigned int tc_ticks_to_bytes(unsigned int rate, unsigned int ticks) { - if (!buffer_hz) { - read_psched(); - } + read_psched(); return (rate * ticks) / ticks_per_s; } @@ -3829,9 +3824,7 @@ tc_ticks_to_bytes(unsigned int rate, unsigned int ticks) static unsigned int tc_bytes_to_ticks(unsigned int rate, unsigned int size) { - if (!buffer_hz) { - read_psched(); - } + read_psched(); return rate ? ((unsigned long long int) ticks_per_s * size) / rate : 0; } @@ -3840,9 +3833,7 @@ tc_bytes_to_ticks(unsigned int rate, unsigned int size) static unsigned int tc_buffer_per_jiffy(unsigned int rate) { - if (!buffer_hz) { - read_psched(); - } + read_psched(); return rate / buffer_hz; } @@ -4198,14 +4189,6 @@ tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes) /* Linux-only functions declared in netdev-linux.h */ -/* Returns a fd for an AF_INET socket or a negative errno value. */ -int -netdev_linux_get_af_inet_sock(void) -{ - int error = netdev_linux_init(); - return error ? -error : af_inet_sock; -} - /* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If * 'enable' is true, the bit is set. Otherwise, it is cleared. */ int @@ -4398,8 +4381,7 @@ get_flags(const struct netdev *dev, unsigned int *flags) int error; *flags = 0; - error = netdev_linux_do_ioctl(dev->name, &ifr, SIOCGIFFLAGS, - "SIOCGIFFLAGS"); + error = af_inet_ifreq_ioctl(dev->name, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS"); if (!error) { *flags = ifr.ifr_flags; } @@ -4412,20 +4394,23 @@ set_flags(const char *name, unsigned int flags) struct ifreq ifr; ifr.ifr_flags = flags; - return netdev_linux_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); + return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); } static int do_get_ifindex(const char *netdev_name) { struct ifreq ifr; + int error; ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); COVERAGE_INC(netdev_get_ifindex); - if (ioctl(af_inet_sock, SIOCGIFINDEX, &ifr) < 0) { + + error = af_inet_ioctl(SIOCGIFINDEX, &ifr); + if (error) { VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s", - netdev_name, ovs_strerror(errno)); - return -errno; + netdev_name, ovs_strerror(error)); + return -error; } return ifr.ifr_ifindex; } @@ -4457,18 +4442,20 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]) { struct ifreq ifr; int hwaddr_family; + int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); COVERAGE_INC(netdev_get_hwaddr); - if (ioctl(af_inet_sock, SIOCGIFHWADDR, &ifr) < 0) { + error = af_inet_ioctl(SIOCGIFHWADDR, &ifr); + if (error) { /* ENODEV probably means that a vif disappeared asynchronously and * hasn't been removed from the database yet, so reduce the log level * to INFO for that case. */ - VLOG(errno == ENODEV ? VLL_INFO : VLL_ERR, + VLOG(error == ENODEV ? VLL_INFO : VLL_ERR, "ioctl(SIOCGIFHWADDR) on %s device failed: %s", - netdev_name, ovs_strerror(errno)); - return errno; + netdev_name, ovs_strerror(error)); + return error; } hwaddr_family = ifr.ifr_hwaddr.sa_family; if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) { @@ -4484,18 +4471,19 @@ set_etheraddr(const char *netdev_name, const uint8_t mac[ETH_ADDR_LEN]) { struct ifreq ifr; + int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN); COVERAGE_INC(netdev_set_hwaddr); - if (ioctl(af_inet_sock, SIOCSIFHWADDR, &ifr) < 0) { + error = af_inet_ioctl(SIOCSIFHWADDR, &ifr); + if (error) { VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s", - netdev_name, ovs_strerror(errno)); - return errno; + netdev_name, ovs_strerror(error)); } - return 0; + return error; } static int @@ -4503,37 +4491,24 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd, int cmd, const char *cmd_name) { struct ifreq ifr; + int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); ifr.ifr_data = (caddr_t) ecmd; ecmd->cmd = cmd; - if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) { - return 0; - } else { - if (errno != EOPNOTSUPP) { + error = af_inet_ioctl(SIOCETHTOOL, &ifr); + if (error) { + if (error != EOPNOTSUPP) { VLOG_WARN_RL(&rl, "ethtool command %s on network device %s " - "failed: %s", cmd_name, name, ovs_strerror(errno)); + "failed: %s", cmd_name, name, ovs_strerror(error)); } else { /* The device doesn't support this operation. That's pretty * common, so there's no point in logging anything. */ } - return errno; } -} - -static int -netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd, - const char *cmd_name) -{ - ovs_strzcpy(ifr->ifr_name, name, sizeof ifr->ifr_name); - if (ioctl(af_inet_sock, cmd, ifr) == -1) { - VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name, - ovs_strerror(errno)); - return errno; - } - return 0; + return error; } static int @@ -4544,9 +4519,10 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip, int error; ifr.ifr_addr.sa_family = AF_INET; - error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name); + error = af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name); if (!error) { - const struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; + const struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, + &ifr.ifr_addr); *ip = sin->sin_addr; } return error; @@ -4556,9 +4532,10 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip, static int af_packet_sock(void) { - static int sock = INT_MIN; + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + static int sock; - if (sock == INT_MIN) { + if (ovsthread_once_start(&once)) { sock = socket(AF_PACKET, SOCK_RAW, 0); if (sock >= 0) { int error = set_nonblocking(sock); @@ -4571,6 +4548,7 @@ af_packet_sock(void) VLOG_ERR("failed to create packet socket: %s", ovs_strerror(errno)); } + ovsthread_once_done(&once); } return sock;