*/
#include <config.h>
+
+#include "netdev-linux.h"
+
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include "socket-util.h"
#include "shash.h"
#include "sset.h"
+#include "timer.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(netdev_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. */
/* 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;
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)
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. */
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
if (is_netdev_linux_class(netdev_class)) {
dev = netdev_dev_linux_cast(base_dev);
- dev->cache_valid = 0;
+ netdev_dev_linux_changed(dev);
}
}
} else {
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);
}
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;
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;
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) {
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
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;
}
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) {
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. */
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;
* 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;
}
}
+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, \
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, \
\
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 =
unsigned int min_burst = tc_buffer_per_jiffy(Bps) + mtu;
return tc_bytes_to_ticks(Bps, MAX(burst_bytes, min_burst));
}
-
+\f
+/* Public utility functions. */
+
+#define COPY_NETDEV_STATS \
+ dst->rx_packets = src->rx_packets; \
+ dst->tx_packets = src->tx_packets; \
+ dst->rx_bytes = src->rx_bytes; \
+ dst->tx_bytes = src->tx_bytes; \
+ dst->rx_errors = src->rx_errors; \
+ dst->tx_errors = src->tx_errors; \
+ dst->rx_dropped = src->rx_dropped; \
+ dst->tx_dropped = src->tx_dropped; \
+ dst->multicast = src->multicast; \
+ dst->collisions = src->collisions; \
+ dst->rx_length_errors = src->rx_length_errors; \
+ dst->rx_over_errors = src->rx_over_errors; \
+ dst->rx_crc_errors = src->rx_crc_errors; \
+ dst->rx_frame_errors = src->rx_frame_errors; \
+ dst->rx_fifo_errors = src->rx_fifo_errors; \
+ dst->rx_missed_errors = src->rx_missed_errors; \
+ dst->tx_aborted_errors = src->tx_aborted_errors; \
+ dst->tx_carrier_errors = src->tx_carrier_errors; \
+ dst->tx_fifo_errors = src->tx_fifo_errors; \
+ dst->tx_heartbeat_errors = src->tx_heartbeat_errors; \
+ dst->tx_window_errors = src->tx_window_errors
+
+/* Copies 'src' into 'dst', performing format conversion in the process. */
+void
+netdev_stats_from_rtnl_link_stats(struct netdev_stats *dst,
+ const struct rtnl_link_stats *src)
+{
+ COPY_NETDEV_STATS;
+}
+
+/* Copies 'src' into 'dst', performing format conversion in the process. */
+void
+netdev_stats_from_rtnl_link_stats64(struct netdev_stats *dst,
+ const struct rtnl_link_stats64 *src)
+{
+ COPY_NETDEV_STATS;
+}
+
+/* Copies 'src' into 'dst', performing format conversion in the process. */
+void
+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;
+}
\f
/* Utility functions. */
struct ofpbuf request;
struct ofpbuf *reply;
struct ifinfomsg *ifi;
- const struct rtnl_link_stats *rtnl_stats;
struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)];
int error;
return EPROTO;
}
- rtnl_stats = nl_attr_get(attrs[IFLA_STATS]);
- stats->rx_packets = rtnl_stats->rx_packets;
- stats->tx_packets = rtnl_stats->tx_packets;
- stats->rx_bytes = rtnl_stats->rx_bytes;
- stats->tx_bytes = rtnl_stats->tx_bytes;
- stats->rx_errors = rtnl_stats->rx_errors;
- stats->tx_errors = rtnl_stats->tx_errors;
- stats->rx_dropped = rtnl_stats->rx_dropped;
- stats->tx_dropped = rtnl_stats->tx_dropped;
- stats->multicast = rtnl_stats->multicast;
- stats->collisions = rtnl_stats->collisions;
- stats->rx_length_errors = rtnl_stats->rx_length_errors;
- stats->rx_over_errors = rtnl_stats->rx_over_errors;
- stats->rx_crc_errors = rtnl_stats->rx_crc_errors;
- stats->rx_frame_errors = rtnl_stats->rx_frame_errors;
- stats->rx_fifo_errors = rtnl_stats->rx_fifo_errors;
- stats->rx_missed_errors = rtnl_stats->rx_missed_errors;
- stats->tx_aborted_errors = rtnl_stats->tx_aborted_errors;
- stats->tx_carrier_errors = rtnl_stats->tx_carrier_errors;
- stats->tx_fifo_errors = rtnl_stats->tx_fifo_errors;
- stats->tx_heartbeat_errors = rtnl_stats->tx_heartbeat_errors;
- stats->tx_window_errors = rtnl_stats->tx_window_errors;
+ netdev_stats_from_rtnl_link_stats(stats, nl_attr_get(attrs[IFLA_STATS]));
ofpbuf_delete(reply);
}
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;
+}