X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=44167da4a4a4c4af6d07b65b921273d6ee0876ea;hb=a5ac7299f8042c01fcdb60c5508047146baf3a16;hp=6541b1e9120fe7aa9557151f6bb15a0ea6f30ab9;hpb=2ee6545f2bff7eb27e8c84965e3ff38dfa909bf6;p=openvswitch diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 6541b1e9..44167da4 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -37,9 +38,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -73,7 +72,6 @@ VLOG_DEFINE_THIS_MODULE(netdev_linux); -COVERAGE_DEFINE(netdev_get_vlan_vid); COVERAGE_DEFINE(netdev_set_policing); COVERAGE_DEFINE(netdev_arp_lookup); COVERAGE_DEFINE(netdev_get_ifindex); @@ -114,9 +112,8 @@ enum { VALID_IN4 = 1 << 2, VALID_IN6 = 1 << 3, VALID_MTU = 1 << 4, - VALID_CARRIER = 1 << 5, - VALID_POLICING = 1 << 6, - VALID_HAVE_VPORT_STATS = 1 << 7 + VALID_POLICING = 1 << 5, + VALID_HAVE_VPORT_STATS = 1 << 6 }; struct tap_state { @@ -367,7 +364,8 @@ struct netdev_dev_linux { struct in_addr address, netmask; struct in6_addr in6; int mtu; - int carrier; + bool carrier; + long long int carrier_resets; uint32_t kbits_rate; /* Policing data. */ uint32_t kbits_burst; bool have_vport_stats; @@ -413,6 +411,7 @@ 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 get_carrier_via_sysfs(const char *name, bool *carrier); static int af_packet_sock(void); static void netdev_linux_miimon_run(void); static void netdev_linux_miimon_wait(void); @@ -503,6 +502,12 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, if (is_netdev_linux_class(netdev_class)) { dev = netdev_dev_linux_cast(base_dev); + + if (dev->carrier != change->running) { + dev->carrier = change->running; + dev->carrier_resets++; + } + netdev_dev_linux_changed(dev); } } @@ -513,7 +518,16 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, shash_init(&device_shash); netdev_dev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { + bool carrier; + dev = node->data; + + get_carrier_via_sysfs(node->name, &carrier); + if (dev->carrier != carrier) { + dev->carrier = carrier; + dev->carrier_resets++; + } + netdev_dev_linux_changed(dev); } shash_destroy(&device_shash); @@ -542,6 +556,7 @@ netdev_linux_create(const struct netdev_class *class, const char *name, netdev_dev = xzalloc(sizeof *netdev_dev); netdev_dev->change_seq = 1; netdev_dev_init(&netdev_dev->netdev_dev, name, class); + get_carrier_via_sysfs(name, &netdev_dev->carrier); *netdev_devp = &netdev_dev->netdev_dev; return 0; @@ -696,28 +711,6 @@ netdev_linux_close(struct netdev *netdev_) free(netdev); } -/* Initializes 'sset' with a list of the names of all known network devices. */ -static int -netdev_linux_enumerate(struct sset *sset) -{ - struct if_nameindex *names; - - names = if_nameindex(); - if (names) { - size_t i; - - for (i = 0; names[i].if_name != NULL; i++) { - sset_add(sset, names[i].if_name); - } - if_freenameindex(names); - return 0; - } else { - VLOG_WARN("could not obtain list of network device names: %s", - strerror(errno)); - return errno; - } -} - static int netdev_linux_listen(struct netdev *netdev_) { @@ -1040,62 +1033,20 @@ netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev_)); - int error = 0; - char *fn = NULL; - int fd = -1; if (netdev_dev->miimon_interval > 0) { *carrier = netdev_dev->miimon; - return 0; + } else { + *carrier = netdev_dev->carrier; } - if (!(netdev_dev->cache_valid & VALID_CARRIER)) { - char line[8]; - int retval; - - fn = xasprintf("/sys/class/net/%s/carrier", - netdev_get_name(netdev_)); - fd = open(fn, O_RDONLY); - if (fd < 0) { - error = errno; - VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(error)); - goto exit; - } - - retval = read(fd, line, sizeof line); - if (retval < 0) { - error = errno; - if (error == EINVAL) { - /* This is the normal return value when we try to check carrier - * if the network device is not up. */ - } else { - VLOG_WARN_RL(&rl, "%s: read failed: %s", fn, strerror(error)); - } - goto exit; - } else if (retval == 0) { - error = EPROTO; - VLOG_WARN_RL(&rl, "%s: unexpected end of file", fn); - goto exit; - } - - if (line[0] != '0' && line[0] != '1') { - error = EPROTO; - VLOG_WARN_RL(&rl, "%s: value is %c (expected 0 or 1)", - fn, line[0]); - goto exit; - } - netdev_dev->carrier = line[0] != '0'; - netdev_dev->cache_valid |= VALID_CARRIER; - } - *carrier = netdev_dev->carrier; - error = 0; + return 0; +} -exit: - if (fd >= 0) { - close(fd); - } - free(fn); - return error; +static long long int +netdev_linux_get_carrier_resets(const struct netdev *netdev) +{ + return netdev_dev_linux_cast(netdev_get_dev(netdev))->carrier_resets; } static int @@ -1592,60 +1543,6 @@ netdev_linux_set_advertisements(struct netdev *netdev, uint32_t advertise) ETHTOOL_SSET, "ETHTOOL_SSET"); } -/* If 'netdev_name' is the name of a VLAN network device (e.g. one created with - * vconfig(8)), sets '*vlan_vid' to the VLAN VID associated with that device - * and returns 0. Otherwise returns a errno value (specifically ENOENT if - * 'netdev_name' is the name of a network device that is not a VLAN device) and - * sets '*vlan_vid' to -1. */ -static int -netdev_linux_get_vlan_vid(const struct netdev *netdev, int *vlan_vid) -{ - const char *netdev_name = netdev_get_name(netdev); - struct ds line = DS_EMPTY_INITIALIZER; - FILE *stream = NULL; - int error; - char *fn; - - COVERAGE_INC(netdev_get_vlan_vid); - fn = xasprintf("/proc/net/vlan/%s", netdev_name); - stream = fopen(fn, "r"); - if (!stream) { - error = errno; - goto done; - } - - if (ds_get_line(&line, stream)) { - if (ferror(stream)) { - error = errno; - VLOG_ERR_RL(&rl, "error reading \"%s\": %s", fn, strerror(errno)); - } else { - error = EPROTO; - VLOG_ERR_RL(&rl, "unexpected end of file reading \"%s\"", fn); - } - goto done; - } - - if (!sscanf(ds_cstr(&line), "%*s VID: %d", vlan_vid)) { - error = EPROTO; - VLOG_ERR_RL(&rl, "parse error reading \"%s\" line 1: \"%s\"", - fn, ds_cstr(&line)); - goto done; - } - - error = 0; - -done: - free(fn); - if (stream) { - fclose(stream); - } - ds_destroy(&line); - if (error) { - *vlan_vid = -1; - } - return error; -} - #define POLICE_ADD_CMD "/sbin/tc qdisc add dev %s handle ffff: ingress" #define POLICE_CONFIG_CMD "/sbin/tc filter add dev %s parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate %dkbit burst %dk mtu 65535 drop flowid :1" @@ -2342,7 +2239,7 @@ 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, GET_STATS, SET_STATS) \ +#define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS) \ { \ NAME, \ \ @@ -2358,8 +2255,6 @@ netdev_linux_change_seq(const struct netdev *netdev) netdev_linux_open, \ netdev_linux_close, \ \ - ENUMERATE, \ - \ netdev_linux_listen, \ netdev_linux_recv, \ netdev_linux_recv_wait, \ @@ -2374,13 +2269,13 @@ netdev_linux_change_seq(const struct netdev *netdev) netdev_linux_set_mtu, \ netdev_linux_get_ifindex, \ netdev_linux_get_carrier, \ + netdev_linux_get_carrier_resets, \ netdev_linux_set_miimon_interval, \ GET_STATS, \ SET_STATS, \ \ netdev_linux_get_features, \ netdev_linux_set_advertisements, \ - netdev_linux_get_vlan_vid, \ \ netdev_linux_set_policing, \ netdev_linux_get_qos_types, \ @@ -2411,7 +2306,6 @@ const struct netdev_class netdev_linux_class = NETDEV_LINUX_CLASS( "system", netdev_linux_create, - netdev_linux_enumerate, netdev_linux_get_stats, NULL); /* set_stats */ @@ -2419,7 +2313,6 @@ const struct netdev_class netdev_tap_class = NETDEV_LINUX_CLASS( "tap", netdev_linux_create_tap, - NULL, /* enumerate */ netdev_pseudo_get_stats, NULL); /* set_stats */ @@ -2427,7 +2320,6 @@ const struct netdev_class netdev_internal_class = NETDEV_LINUX_CLASS( "internal", netdev_linux_create, - NULL, /* enumerate */ netdev_pseudo_get_stats, netdev_vport_set_stats); @@ -4038,6 +3930,63 @@ tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes) return tc_bytes_to_ticks(Bps, MAX(burst_bytes, min_burst)); } +/* 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 +netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, + const char *flag_name, bool enable) +{ + const char *netdev_name = netdev_get_name(netdev); + struct ethtool_value evalue; + uint32_t new_flags; + int error; + + memset(&evalue, 0, sizeof evalue); + error = netdev_linux_do_ethtool(netdev_name, + (struct ethtool_cmd *)&evalue, + ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); + if (error) { + return error; + } + + evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0); + error = netdev_linux_do_ethtool(netdev_name, + (struct ethtool_cmd *)&evalue, + ETHTOOL_SFLAGS, "ETHTOOL_SFLAGS"); + if (error) { + return error; + } + + memset(&evalue, 0, sizeof evalue); + error = netdev_linux_do_ethtool(netdev_name, + (struct ethtool_cmd *)&evalue, + ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); + if (error) { + return error; + } + + if (new_flags != evalue.data) { + VLOG_WARN_RL(&rl, "attempt to %s ethtool %s flag on network " + "device %s failed", enable ? "enable" : "disable", + flag_name, netdev_name); + return EOPNOTSUPP; + } + + return 0; +} + +/* Utility functions. */ + /* Copies 'src' into 'dst', performing format conversion in the process. */ static void netdev_stats_from_rtnl_link_stats(struct netdev_stats *dst, @@ -4066,9 +4015,6 @@ netdev_stats_from_rtnl_link_stats(struct netdev_stats *dst, dst->tx_window_errors = src->tx_window_errors; } - -/* Utility functions. */ - static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats) { @@ -4176,6 +4122,58 @@ get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats) return ENODEV; } +static int +get_carrier_via_sysfs(const char *name, bool *carrier) +{ + char line[8]; + int retval; + + int error = 0; + char *fn = NULL; + int fd = -1; + + *carrier = false; + + fn = xasprintf("/sys/class/net/%s/carrier", name); + fd = open(fn, O_RDONLY); + if (fd < 0) { + error = errno; + VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(error)); + goto exit; + } + + retval = read(fd, line, sizeof line); + if (retval < 0) { + error = errno; + if (error == EINVAL) { + /* This is the normal return value when we try to check carrier if + * the network device is not up. */ + } else { + VLOG_WARN_RL(&rl, "%s: read failed: %s", fn, strerror(error)); + } + goto exit; + } else if (retval == 0) { + error = EPROTO; + VLOG_WARN_RL(&rl, "%s: unexpected end of file", fn); + goto exit; + } + + if (line[0] != '0' && line[0] != '1') { + error = EPROTO; + VLOG_WARN_RL(&rl, "%s: value is %c (expected 0 or 1)", fn, line[0]); + goto exit; + } + *carrier = line[0] != '0'; + error = 0; + +exit: + if (fd >= 0) { + close(fd); + } + free(fn); + return error; +} + static int get_flags(const struct netdev *netdev, int *flags) { @@ -4303,51 +4301,6 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd, } } -/* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If - * 'enable' is true, the bit is set. Otherwise, it is cleared. */ -int -netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, - const char *flag_name, bool enable) -{ - const char *netdev_name = netdev_get_name(netdev); - struct ethtool_value evalue; - uint32_t new_flags; - int error; - - memset(&evalue, 0, sizeof evalue); - error = netdev_linux_do_ethtool(netdev_name, - (struct ethtool_cmd *)&evalue, - ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); - if (error) { - return error; - } - - evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0); - error = netdev_linux_do_ethtool(netdev_name, - (struct ethtool_cmd *)&evalue, - ETHTOOL_SFLAGS, "ETHTOOL_SFLAGS"); - if (error) { - return error; - } - - memset(&evalue, 0, sizeof evalue); - error = netdev_linux_do_ethtool(netdev_name, - (struct ethtool_cmd *)&evalue, - ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); - if (error) { - return error; - } - - if (new_flags != evalue.data) { - VLOG_WARN_RL(&rl, "attempt to %s ethtool %s flag on network " - "device %s failed", enable ? "enable" : "disable", - flag_name, netdev_name); - return EOPNOTSUPP; - } - - return 0; -} - static int netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd, const char *cmd_name)