X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=1428ce6617fc76647ae60d4b105e1a3aa1966a56;hb=f915f1a8ca180828983ef22cf2fd21b8f010b972;hp=ed97eb3c509742675bccf5ba57cb68f85701dd11;hpb=2fe27d5ad27f3c7879ea696209bcf9702d9b7109;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index ed97eb3c5..1428ce661 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010 Nicira Networks. + * Copyright (c) 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include "coverage.h" +#include "dpif-linux.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "hash.h" @@ -59,6 +61,7 @@ #include "packets.h" #include "poll-loop.h" #include "rtnetlink.h" +#include "rtnetlink-link.h" #include "socket-util.h" #include "shash.h" #include "svec.h" @@ -444,7 +447,7 @@ netdev_linux_init(void) /* Create rtnetlink socket. */ if (!status) { - status = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock); + status = nl_sock_create(NETLINK_ROUTE, &rtnl_sock); if (status) { VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s", strerror(status)); @@ -457,17 +460,17 @@ netdev_linux_init(void) static void netdev_linux_run(void) { - rtnetlink_notifier_run(); + rtnetlink_link_notifier_run(); } static void netdev_linux_wait(void) { - rtnetlink_notifier_wait(); + rtnetlink_link_notifier_wait(); } static void -netdev_linux_cache_cb(const struct rtnetlink_change *change, +netdev_linux_cache_cb(const struct rtnetlink_link_change *change, void *aux OVS_UNUSED) { struct netdev_dev_linux *dev; @@ -511,8 +514,8 @@ netdev_linux_create(const struct netdev_class *class, } if (!cache_notifier_refcount) { - error = rtnetlink_notifier_register(&netdev_linux_cache_notifier, - netdev_linux_cache_cb, NULL); + error = rtnetlink_link_notifier_register(&netdev_linux_cache_notifier, + netdev_linux_cache_cb, NULL); if (error) { return error; } @@ -520,7 +523,7 @@ netdev_linux_create(const struct netdev_class *class, cache_notifier_refcount++; netdev_dev = xzalloc(sizeof *netdev_dev); - netdev_dev_init(&netdev_dev->netdev_dev, name, class); + netdev_dev_init(&netdev_dev->netdev_dev, name, args, class); *netdev_devp = &netdev_dev->netdev_dev; return 0; @@ -574,7 +577,7 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, goto error; } - netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_class); + netdev_dev_init(&netdev_dev->netdev_dev, name, args, &netdev_tap_class); *netdev_devp = &netdev_dev->netdev_dev; return 0; @@ -598,20 +601,22 @@ static void netdev_linux_destroy(struct netdev_dev *netdev_dev_) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_); - const char *type = netdev_dev_get_type(netdev_dev_); + const struct netdev_class *class = netdev_dev_get_class(netdev_dev_); if (netdev_dev->tc && netdev_dev->tc->ops->tc_destroy) { netdev_dev->tc->ops->tc_destroy(netdev_dev->tc); } - if (!strcmp(type, "system")) { + if (class == &netdev_linux_class || class == &netdev_internal_class) { cache_notifier_refcount--; if (!cache_notifier_refcount) { - rtnetlink_notifier_unregister(&netdev_linux_cache_notifier); + rtnetlink_link_notifier_unregister(&netdev_linux_cache_notifier); } - } else if (!strcmp(type, "tap")) { + } else if (class == &netdev_tap_class) { destroy_tap(netdev_dev); + } else { + NOT_REACHED(); } free(netdev_dev); @@ -1004,6 +1009,66 @@ exit: return error; } +static int +netdev_linux_do_miimon(const struct netdev *netdev, 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); + memcpy(data, &ifr.ifr_data, sizeof *data); + + return error; +} + +static int +netdev_linux_get_miimon(const struct netdev *netdev, 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); + 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", + &data); + + if (!error) { + *miimon = !!(data.val_out & BMSR_LSTATUS); + } else { + VLOG_WARN_RL(&rl, "%s: failed to query MII", name); + } + } else { + struct ethtool_cmd ecmd; + + VLOG_DBG_RL(&rl, "%s: failed to query MII, falling back to ethtool", + name); + + memset(&ecmd, 0, sizeof ecmd); + error = netdev_linux_do_ethtool(name, &ecmd, ETHTOOL_GLINK, + "ETHTOOL_GLINK"); + if (!error) { + struct ethtool_value eval; + + memcpy(&eval, &ecmd, sizeof eval); + *miimon = !!eval.data; + } else { + VLOG_WARN_RL(&rl, "%s: ethtool link status failed", name); + } + } + + return error; +} + /* 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. */ @@ -1041,22 +1106,8 @@ netdev_linux_update_is_pseudo(struct netdev_dev_linux *netdev_dev) const char *type = netdev_dev_get_type(&netdev_dev->netdev_dev); netdev_dev->is_tap = !strcmp(type, "tap"); - netdev_dev->is_internal = false; - if (!netdev_dev->is_tap) { - struct ethtool_drvinfo drvinfo; - int error; - - memset(&drvinfo, 0, sizeof drvinfo); - error = netdev_linux_do_ethtool(name, - (struct ethtool_cmd *)&drvinfo, - ETHTOOL_GDRVINFO, - "ETHTOOL_GDRVINFO"); - - if (!error && !strcmp(drvinfo.driver, "openvswitch")) { - netdev_dev->is_internal = true; - } - } - + netdev_dev->is_internal = (!netdev_dev->is_tap + && dpif_linux_is_internal_device(name)); netdev_dev->cache_valid |= VALID_IS_PSEUDO; } } @@ -1137,7 +1188,7 @@ netdev_linux_get_stats(const struct netdev *netdev_, * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if * successful, otherwise a positive errno value. */ static int -netdev_linux_get_features(struct netdev *netdev, +netdev_linux_get_features(const struct netdev *netdev, uint32_t *current, uint32_t *advertised, uint32_t *supported, uint32_t *peer) { @@ -1961,6 +2012,26 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop, return ENXIO; } +static int +netdev_linux_get_status(const struct netdev *netdev, struct shash *sh) +{ + struct ethtool_drvinfo drvinfo; + int error; + + memset(&drvinfo, 0, sizeof drvinfo); + error = netdev_linux_do_ethtool(netdev_get_name(netdev), + (struct ethtool_cmd *)&drvinfo, + ETHTOOL_GDRVINFO, + "ETHTOOL_GDRVINFO"); + if (!error) { + shash_add(sh, "driver_name", xstrdup(drvinfo.driver)); + shash_add(sh, "driver_version", xstrdup(drvinfo.version)); + shash_add(sh, "firmware_version", xstrdup(drvinfo.fw_version)); + } + + return error; +} + /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be * successfully retrieved, it stores the corresponding MAC address in 'mac' and * returns 0. Otherwise, it returns a positive errno value; in particular, @@ -2047,7 +2118,7 @@ poll_notify(struct list *list) } static void -netdev_linux_poll_cb(const struct rtnetlink_change *change, +netdev_linux_poll_cb(const struct rtnetlink_link_change *change, void *aux OVS_UNUSED) { if (change) { @@ -2074,8 +2145,9 @@ netdev_linux_poll_add(struct netdev *netdev, struct list *list; if (shash_is_empty(&netdev_linux_notifiers)) { - int error = rtnetlink_notifier_register(&netdev_linux_poll_notifier, - netdev_linux_poll_cb, NULL); + int error; + error = rtnetlink_link_notifier_register(&netdev_linux_poll_notifier, + netdev_linux_poll_cb, NULL); if (error) { return error; } @@ -2115,7 +2187,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) /* If that was the last notifier, unregister. */ if (shash_is_empty(&netdev_linux_notifiers)) { - rtnetlink_notifier_unregister(&netdev_linux_poll_notifier); + rtnetlink_link_notifier_unregister(&netdev_linux_poll_notifier); } } @@ -2129,7 +2201,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) \ CREATE, \ netdev_linux_destroy, \ - NULL, /* reconfigure */ \ + NULL, /* set_config */ \ \ netdev_linux_open, \ netdev_linux_close, \ @@ -2148,6 +2220,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_get_stats, \ SET_STATS, \ \ @@ -2172,6 +2245,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) netdev_linux_get_in6, \ netdev_linux_add_router, \ netdev_linux_get_next_hop, \ + netdev_linux_get_status, \ netdev_linux_arp_lookup, \ \ netdev_linux_update_flags, \ @@ -2291,6 +2365,11 @@ htb_setup_class__(struct netdev *netdev, unsigned int handle, int mtu; netdev_get_mtu(netdev, &mtu); + if (mtu == INT_MAX) { + VLOG_WARN_RL(&rl, "cannot set up HTB on device %s that lacks MTU", + netdev_get_name(netdev)); + return EINVAL; + } memset(&opt, 0, sizeof opt); tc_fill_rate(&opt.rate, class->min_rate, mtu); @@ -2410,6 +2489,13 @@ htb_parse_class_details__(struct netdev *netdev, const char *priority_s = shash_find_data(details, "priority"); int mtu; + netdev_get_mtu(netdev, &mtu); + if (mtu == INT_MAX) { + VLOG_WARN_RL(&rl, "cannot parse HTB class on device %s that lacks MTU", + netdev_get_name(netdev)); + return EINVAL; + } + /* min-rate. Don't allow a min-rate below 1500 bytes/s. */ if (!min_rate_s) { /* min-rate is required. */ @@ -2435,7 +2521,6 @@ htb_parse_class_details__(struct netdev *netdev, * doesn't include the Ethernet header, we need to add at least 14 (18?) to * the MTU. We actually add 64, instead of 14, as a guard against * additional headers get tacked on somewhere that we're not aware of. */ - netdev_get_mtu(netdev, &mtu); hc->burst = burst_s ? strtoull(burst_s, NULL, 10) / 8 : 0; hc->burst = MAX(hc->burst, mtu + 64);