X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=eda12760376cd6714b9dde41838641bfd57be54e;hb=ac4d3bcb46fa0acd0b63f79449432df28569f74f;hp=384fdafee96818cb74e703ca7c5495fa705cb8f7;hpb=d39808227b8a8e794a7cb0b990f4fcb0f5daadf5;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 384fdafee..eda127603 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -68,6 +68,7 @@ #include "socket-util.h" #include "shash.h" #include "sset.h" +#include "timer.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(netdev_linux); @@ -345,6 +346,11 @@ struct netdev_dev_linux { struct shash_node *shash_node; unsigned int cache_valid; + unsigned int change_seq; + + bool miimon; /* Link status of last poll. */ + long long int miimon_interval; /* Miimon Poll rate. Disabled if <= 0. */ + struct timer miimon_timer; /* The following are figured out "on demand" only. They are only valid * when the corresponding VALID_* bit in 'cache_valid' is set. */ @@ -373,7 +379,6 @@ struct netdev_linux { /* Sockets used for ioctl operations. */ static int af_inet_sock = -1; /* AF_INET, SOCK_DGRAM. */ -static int af_packet_sock = -1; /* AF_PACKET, SOCK_RAW. */ /* A Netlink routing socket that is not subscribed to any multicast groups. */ static struct nl_sock *rtnl_sock; @@ -411,6 +416,10 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family, const uint8_t[ETH_ADDR_LEN]); static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats); static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats); +static int af_packet_sock(void); +static void poll_notify(struct list *); +static void netdev_linux_miimon_run(void); +static void netdev_linux_miimon_wait(void); static bool is_netdev_linux_class(const struct netdev_class *netdev_class) @@ -447,15 +456,6 @@ netdev_linux_init(void) status = af_inet_sock >= 0 ? 0 : errno; if (status) { VLOG_ERR("failed to create inet socket: %s", strerror(status)); - } else { - /* Create AF_PACKET socket. */ - af_packet_sock = socket(AF_PACKET, SOCK_RAW, 0); - status = af_packet_sock >= 0 ? 0 : errno; - if (status) { - VLOG_ERR("failed to create packet socket: %s", - strerror(status)); - } - set_nonblocking(af_packet_sock); } /* Create rtnetlink socket. */ @@ -474,12 +474,24 @@ static void netdev_linux_run(void) { rtnetlink_link_notifier_run(); + netdev_linux_miimon_run(); } static void netdev_linux_wait(void) { rtnetlink_link_notifier_wait(); + netdev_linux_miimon_wait(); +} + +static void +netdev_dev_linux_changed(struct netdev_dev_linux *dev) +{ + dev->change_seq++; + if (!dev->change_seq) { + dev->change_seq++; + } + dev->cache_valid = 0; } static void @@ -495,7 +507,7 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, if (is_netdev_linux_class(netdev_class)) { dev = netdev_dev_linux_cast(base_dev); - dev->cache_valid = 0; + netdev_dev_linux_changed(dev); } } } else { @@ -506,7 +518,7 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, netdev_dev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { dev = node->data; - dev->cache_valid = 0; + netdev_dev_linux_changed(dev); } shash_destroy(&device_shash); } @@ -536,6 +548,7 @@ netdev_linux_create(const struct netdev_class *class, cache_notifier_refcount++; netdev_dev = xzalloc(sizeof *netdev_dev); + netdev_dev->change_seq = 1; netdev_dev_init(&netdev_dev->netdev_dev, name, args, class); *netdev_devp = &netdev_dev->netdev_dev; @@ -682,7 +695,8 @@ netdev_linux_open(struct netdev_dev *netdev_dev_, int ethertype, protocol = (ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL : ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2 : ethertype); - netdev->fd = socket(PF_PACKET, SOCK_RAW, htons(protocol)); + netdev->fd = socket(PF_PACKET, SOCK_RAW, + (OVS_FORCE int) htons(protocol)); if (netdev->fd < 0) { error = errno; goto error; @@ -843,6 +857,12 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) struct iovec iov; int ifindex; int error; + int sock; + + sock = af_packet_sock(); + if (sock < 0) { + return sock; + } error = get_ifindex(netdev_, &ifindex); if (error) { @@ -866,7 +886,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) msg.msg_controllen = 0; msg.msg_flags = 0; - retval = sendmsg(af_packet_sock, &msg, 0); + retval = sendmsg(sock, &msg, 0); } else { /* Use the netdev's own fd to send to this device. This is * essential for tap devices, because packets sent to a tap device @@ -1006,6 +1026,11 @@ netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) char *fn = NULL; int fd = -1; + if (netdev_dev->miimon_interval > 0) { + *carrier = netdev_dev->miimon; + return 0; + } + if (!(netdev_dev->cache_valid & VALID_CARRIER)) { char line[8]; int retval; @@ -1056,36 +1081,34 @@ exit: } static int -netdev_linux_do_miimon(const struct netdev *netdev, int cmd, - const char *cmd_name, struct mii_ioctl_data *data) +netdev_linux_do_miimon(const char *name, int cmd, const char *cmd_name, + struct mii_ioctl_data *data) { struct ifreq ifr; int error; memset(&ifr, 0, sizeof ifr); memcpy(&ifr.ifr_data, data, sizeof *data); - error = netdev_linux_do_ioctl(netdev_get_name(netdev), - &ifr, cmd, cmd_name); + error = netdev_linux_do_ioctl(name, &ifr, cmd, cmd_name); memcpy(data, &ifr.ifr_data, sizeof *data); return error; } static int -netdev_linux_get_miimon(const struct netdev *netdev, bool *miimon) +netdev_linux_get_miimon(const char *name, bool *miimon) { - const char *name = netdev_get_name(netdev); struct mii_ioctl_data data; int error; *miimon = false; memset(&data, 0, sizeof data); - error = netdev_linux_do_miimon(netdev, SIOCGMIIPHY, "SIOCGMIIPHY", &data); + error = netdev_linux_do_miimon(name, SIOCGMIIPHY, "SIOCGMIIPHY", &data); if (!error) { /* data.phy_id is filled out by previous SIOCGMIIPHY miimon call. */ data.reg_num = MII_BMSR; - error = netdev_linux_do_miimon(netdev, SIOCGMIIREG, "SIOCGMIIREG", + error = netdev_linux_do_miimon(name, SIOCGMIIREG, "SIOCGMIIREG", &data); if (!error) { @@ -1115,6 +1138,76 @@ netdev_linux_get_miimon(const struct netdev *netdev, bool *miimon) return error; } +static int +netdev_linux_set_miimon_interval(struct netdev *netdev_, + long long int interval) +{ + struct netdev_dev_linux *netdev_dev; + + netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev_)); + + interval = interval > 0 ? MAX(interval, 100) : 0; + if (netdev_dev->miimon_interval != interval) { + netdev_dev->miimon_interval = interval; + timer_set_expired(&netdev_dev->miimon_timer); + } + + return 0; +} + +static void +netdev_linux_miimon_run(void) +{ + struct shash device_shash; + struct shash_node *node; + + shash_init(&device_shash); + netdev_dev_get_devices(&netdev_linux_class, &device_shash); + SHASH_FOR_EACH (node, &device_shash) { + struct netdev_dev_linux *dev = node->data; + bool miimon; + + if (dev->miimon_interval <= 0 || !timer_expired(&dev->miimon_timer)) { + continue; + } + + netdev_linux_get_miimon(dev->netdev_dev.name, &miimon); + if (miimon != dev->miimon) { + struct list *list; + + dev->miimon = miimon; + list = shash_find_data(&netdev_linux_notifiers, + dev->netdev_dev.name); + if (list) { + poll_notify(list); + } + netdev_dev_linux_changed(dev); + } + + timer_set_duration(&dev->miimon_timer, dev->miimon_interval); + } + + shash_destroy(&device_shash); +} + +static void +netdev_linux_miimon_wait(void) +{ + struct shash device_shash; + struct shash_node *node; + + shash_init(&device_shash); + netdev_dev_get_devices(&netdev_linux_class, &device_shash); + SHASH_FOR_EACH (node, &device_shash) { + struct netdev_dev_linux *dev = node->data; + + if (dev->miimon_interval > 0) { + timer_wait(&dev->miimon_timer); + } + } + shash_destroy(&device_shash); +} + /* Check whether we can we use RTM_GETLINK to get network device statistics. * In pre-2.6.19 kernels, this was only available if wireless extensions were * enabled. */ @@ -2017,7 +2110,7 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop, while (fgets(line, sizeof line, stream)) { if (++ln >= 2) { char iface[17]; - uint32_t dest, gateway, mask; + ovs_be32 dest, gateway, mask; int refcnt, metric, mtu; unsigned int flags, use, window, irtt; @@ -2084,7 +2177,7 @@ netdev_linux_get_status(const struct netdev *netdev, struct shash *sh) * ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. */ static int netdev_linux_arp_lookup(const struct netdev *netdev, - uint32_t ip, uint8_t mac[ETH_ADDR_LEN]) + ovs_be32 ip, uint8_t mac[ETH_ADDR_LEN]) { struct arpreq r; struct sockaddr_in sin; @@ -2238,6 +2331,12 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) } } +static unsigned int +netdev_linux_change_seq(const struct netdev *netdev) +{ + return netdev_dev_linux_cast(netdev_get_dev(netdev))->change_seq; +} + #define NETDEV_LINUX_CLASS(NAME, CREATE, ENUMERATE, SET_STATS) \ { \ NAME, \ @@ -2267,7 +2366,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) netdev_linux_get_mtu, \ netdev_linux_get_ifindex, \ netdev_linux_get_carrier, \ - netdev_linux_get_miimon, \ + netdev_linux_set_miimon_interval, \ netdev_linux_get_stats, \ SET_STATS, \ \ @@ -2298,7 +2397,8 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) netdev_linux_update_flags, \ \ netdev_linux_poll_add, \ - netdev_linux_poll_remove \ + netdev_linux_poll_remove, \ + netdev_linux_change_seq \ } const struct netdev_class netdev_linux_class = @@ -3976,6 +4076,8 @@ netdev_stats_to_rtnl_link_stats64(struct rtnl_link_stats64 *dst, const struct netdev_stats *src) { COPY_NETDEV_STATS; + dst->rx_compressed = 0; + dst->tx_compressed = 0; } /* Utility functions. */ @@ -4238,3 +4340,22 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip, } return error; } + +/* Returns an AF_PACKET raw socket or a negative errno value. */ +static int +af_packet_sock(void) +{ + static int sock = INT_MIN; + + if (sock == INT_MIN) { + sock = socket(AF_PACKET, SOCK_RAW, 0); + if (sock >= 0) { + set_nonblocking(sock); + } else { + sock = -errno; + VLOG_ERR("failed to create packet socket: %s", strerror(errno)); + } + } + + return sock; +}