From: Jesse Gross Date: Tue, 12 Jan 2010 21:01:43 +0000 (-0500) Subject: netdev: Fully handle netdev lifecycle through refcounting. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=149f577a;p=openvswitch netdev: Fully handle netdev lifecycle through refcounting. This builds on earlier work that implemented netdev object refcounting. However, rather than requiring explicit create and destroy calls, these operations are now performed automatically based on the referenece count. This is important because in certain situations it is not possible to know whether a netdev has already been created. A workaround existed (which looked fairly similar to this paradigm) but introduced it's own issues. This simplifies and unifies the API. --- diff --git a/lib/dhcp-client.c b/lib/dhcp-client.c index 720cd2fa..fb6835c1 100644 --- a/lib/dhcp-client.c +++ b/lib/dhcp-client.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -151,12 +151,19 @@ dhclient_create(const char *netdev_name, void *aux, struct dhclient **cli_) { struct dhclient *cli; + struct netdev_options netdev_options; struct netdev *netdev; int error; *cli_ = NULL; - error = netdev_open(netdev_name, ETH_TYPE_IP, &netdev); + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = netdev_name; + netdev_options.ethertype = ETH_TYPE_IP; + netdev_options.may_create = true; + netdev_options.may_open = true; + + error = netdev_open(&netdev_options, &netdev); /* XXX install socket filter to catch only DHCP packets. */ if (error) { VLOG_ERR("could not open %s network device: %s", diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 816d4025..1d496177 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Nicira Networks. + * Copyright (c) 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -237,7 +237,7 @@ create_dp_netdev(const char *name, int dp_idx, struct dpif **dpifp) error = do_add_port(dp, name, ODP_PORT_INTERNAL, ODPP_LOCAL); if (error) { dp_netdev_free(dp); - return error; + return ENODEV; } *dpifp = create_dpif_netdev(dp); @@ -363,6 +363,7 @@ do_add_port(struct dp_netdev *dp, const char *devname, uint16_t flags, { bool internal = (flags & ODP_PORT_INTERNAL) != 0; struct dp_netdev_port *port; + struct netdev_options netdev_options; struct netdev *netdev; int mtu; int error; @@ -370,17 +371,17 @@ do_add_port(struct dp_netdev *dp, const char *devname, uint16_t flags, /* XXX reject devices already in some dp_netdev. */ /* Open and validate network device. */ - if (!internal) { - error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev); + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = devname; + netdev_options.ethertype = NETDEV_ETH_TYPE_ANY; + netdev_options.may_create = true; + if (internal) { + netdev_options.type = "tap"; } else { - error = netdev_create(devname, "tap", NULL); - if (!error) { - error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev); - if (error) { - netdev_destroy(devname); - } - } + netdev_options.may_open = true; } + + error = netdev_open(&netdev_options, &netdev); if (error) { return error; } @@ -487,9 +488,7 @@ do_del_port(struct dp_netdev *dp, uint16_t port_no) name = xstrdup(netdev_get_name(port->netdev)); netdev_close(port->netdev); - if (port->internal) { - netdev_destroy(name); - } + free(name); free(port); diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 18d32742..e5e4adb1 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Nicira Networks. + * Copyright (c) 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,30 +74,8 @@ #define ADVERTISED_Asym_Pause (1 << 14) #endif -struct tap_state { - int tap_fd; /* File descriptor for TAP device. */ -}; - -/* Provider-specific netdev object. Netdev objects are devices that are - * created by the netdev library through a netdev_create() call. */ -struct netdev_obj_linux { - struct netdev_obj netdev_obj; - union { - struct tap_state tap; - } state; -}; - -struct netdev_linux { - struct netdev netdev; - - /* File descriptors. For ordinary network devices, the two fds below are - * the same; for tap devices, they differ. */ - int netdev_fd; /* Network device. */ - int tap_fd; /* TAP character device, if any, otherwise the - * network device. */ - - struct netdev_linux_cache *cache; -}; +static struct rtnetlink_notifier netdev_linux_cache_notifier; +static struct shash cache_map = SHASH_INITIALIZER(&cache_map); enum { VALID_IFINDEX = 1 << 0, @@ -109,11 +87,15 @@ enum { VALID_IS_INTERNAL = 1 << 6 }; -/* Cached network device information. */ -struct netdev_linux_cache { +struct tap_state { + int fd; +}; + +struct netdev_dev_linux { + struct netdev_dev netdev_dev; + struct shash_node *shash_node; - unsigned int valid; - int ref_cnt; + unsigned int cache_valid; int ifindex; uint8_t etheraddr[ETH_ADDR_LEN]; @@ -122,10 +104,21 @@ struct netdev_linux_cache { int mtu; int carrier; bool is_internal; + + union { + struct tap_state tap; + } state; }; -static struct shash cache_map = SHASH_INITIALIZER(&cache_map); -static struct rtnetlink_notifier netdev_linux_cache_notifier; +struct netdev_linux { + struct netdev netdev; + + /* File descriptors. For ordinary network devices, the two fds below are + * the same; for tap devices, they differ. */ + int netdev_fd; /* Network device. */ + int tap_fd; /* TAP character device, if any, otherwise the + * network device. */ +}; /* An AF_INET socket (used for ioctl operations). */ static int af_inet_sock = -1; @@ -162,10 +155,10 @@ static struct rtnetlink_notifier netdev_linux_poll_notifier; * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); -static int netdev_linux_do_ethtool(struct netdev *, struct ethtool_cmd *, +static int netdev_linux_do_ethtool(const struct netdev *, struct ethtool_cmd *, int cmd, const char *cmd_name); -static int netdev_linux_do_ioctl(const struct netdev *, struct ifreq *, - 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 *, int *flagsp); @@ -181,20 +174,21 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family, 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 struct netdev_obj_linux * -netdev_obj_linux_cast(const struct netdev_obj *netdev_obj) +static struct netdev_dev_linux * +netdev_dev_linux_cast(const struct netdev_dev *netdev_dev) { - /* xxx Gross hack! Until we get the class 'type' issue resolved. */ -#if 0 - netdev_obj_assert_class(netdev_obj, &netdev_linux_class); -#endif - return CONTAINER_OF(netdev_obj, struct netdev_obj_linux, netdev_obj); + const char *type = netdev_dev_get_type(netdev_dev); + assert(!strcmp(type, "system") || !strcmp(type, "tap") + || !strcmp(type, "gre")); + return CONTAINER_OF(netdev_dev, struct netdev_dev_linux, netdev_dev); } static struct netdev_linux * netdev_linux_cast(const struct netdev *netdev) { - netdev_assert_class(netdev, &netdev_linux_class); + const char *type = netdev_get_type(netdev); + assert(!strcmp(type, "system") || !strcmp(type, "tap") + || !strcmp(type, "gre")); return CONTAINER_OF(netdev, struct netdev_linux, netdev); } @@ -228,17 +222,17 @@ static void netdev_linux_cache_cb(const struct rtnetlink_change *change, void *aux UNUSED) { - struct netdev_linux_cache *cache; + struct netdev_dev_linux *dev; if (change) { - cache = shash_find_data(&cache_map, change->ifname); - if (cache) { - cache->valid = 0; + dev = shash_find_data(&cache_map, change->ifname); + if (dev) { + dev->cache_valid = 0; } } else { struct shash_node *node; SHASH_FOR_EACH (node, &cache_map) { - cache = node->data; - cache->valid = 0; + dev = node->data; + dev->cache_valid = 0; } } } @@ -461,63 +455,54 @@ error: return error; } -static int -if_up(const char *name) -{ - struct ifreq ifr; - - strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); - ifr.ifr_flags = IFF_UP; - - if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) { - VLOG_DBG_RL(&rl, "%s: failed to bring device up: %s", - name, strerror(errno)); - return errno; - } - - return 0; -} - -/* Creates the netdev object of 'type' with 'name'. */ +/* Creates the netdev device of 'type' with 'name'. */ static int netdev_linux_create_system(const char *name, const char *type UNUSED, - const struct shash *args, bool created) + const struct shash *args, struct netdev_dev **netdev_devp) { - struct netdev_obj_linux *netdev_obj; + struct netdev_dev_linux *netdev_dev; + int error; if (!shash_is_empty(args)) { - VLOG_WARN("arguments for system devices should be empty"); + VLOG_WARN("%s: arguments for system devices should be empty", name); } - netdev_obj = xcalloc(1, sizeof *netdev_obj); - netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_linux_class, - created); + if (shash_is_empty(&cache_map)) { + error = rtnetlink_notifier_register(&netdev_linux_cache_notifier, + netdev_linux_cache_cb, NULL); + if (error) { + return error; + } + } + netdev_dev = xzalloc(sizeof *netdev_dev); + netdev_dev->shash_node = shash_add(&cache_map, name, &netdev_dev); + + netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_linux_class); + *netdev_devp = &netdev_dev->netdev_dev; return 0; } static int netdev_linux_create_tap(const char *name, const char *type UNUSED, - const struct shash *args, bool created) + const struct shash *args, struct netdev_dev **netdev_devp) { - struct netdev_obj_linux *netdev_obj; + struct netdev_dev_linux *netdev_dev; struct tap_state *state; static const char tap_dev[] = "/dev/net/tun"; struct ifreq ifr; int error; if (!shash_is_empty(args)) { - VLOG_WARN("arguments for TAP devices should be empty"); + VLOG_WARN("%s: arguments for TAP devices should be empty", name); } - netdev_obj = xcalloc(1, sizeof *netdev_obj); - netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_tap_class, created); - - state = &netdev_obj->state.tap; + netdev_dev = xzalloc(sizeof *netdev_dev); + state = &netdev_dev->state.tap; /* Open tap device. */ - state->tap_fd = open(tap_dev, O_RDWR); - if (state->tap_fd < 0) { + state->fd = open(tap_dev, O_RDWR); + if (state->fd < 0) { error = errno; VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error)); goto error; @@ -526,7 +511,7 @@ netdev_linux_create_tap(const char *name, const char *type UNUSED, /* Create tap device. */ ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); - if (ioctl(state->tap_fd, TUNSETIFF, &ifr) == -1) { + if (ioctl(state->fd, TUNSETIFF, &ifr) == -1) { VLOG_WARN("%s: creating tap device failed: %s", name, strerror(errno)); error = errno; @@ -534,32 +519,45 @@ netdev_linux_create_tap(const char *name, const char *type UNUSED, } /* Make non-blocking. */ - error = set_nonblocking(state->tap_fd); - if (error) { - goto error; - } - - error = if_up(name); + error = set_nonblocking(state->fd); if (error) { goto error; } + netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_class); + *netdev_devp = &netdev_dev->netdev_dev; return 0; error: - netdev_destroy(name); + free(netdev_dev); return error; } +static int +if_up(const char *name) +{ + struct ifreq ifr; + + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + ifr.ifr_flags = IFF_UP; + + if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) { + VLOG_DBG_RL(&rl, "%s: failed to bring device up: %s", + name, strerror(errno)); + return errno; + } + + return 0; +} + static int netdev_linux_create_gre(const char *name, const char *type UNUSED, - const struct shash *args, bool created) + const struct shash *args, struct netdev_dev **netdev_devp) { - struct netdev_obj_linux *netdev_obj; + struct netdev_dev_linux *netdev_dev; int error; - netdev_obj = xcalloc(1, sizeof *netdev_obj); - netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_gre_class, created); + netdev_dev = xzalloc(sizeof *netdev_dev); error = setup_gre(name, args, true); if (error) { @@ -571,19 +569,20 @@ netdev_linux_create_gre(const char *name, const char *type UNUSED, goto error; } + netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_gre_class); + *netdev_devp = &netdev_dev->netdev_dev; return 0; error: - netdev_destroy(name); + free(netdev_dev); return error; } static int -netdev_linux_reconfigure_gre(struct netdev_obj *netdev_obj_, +netdev_linux_reconfigure_gre(struct netdev_dev *netdev_dev_, const struct shash *args) { - struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_); - const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj); + const char *name = netdev_dev_get_name(netdev_dev_); return setup_gre(name, args, false); } @@ -591,12 +590,12 @@ netdev_linux_reconfigure_gre(struct netdev_obj *netdev_obj_, /* The arguments are marked as unused to prevent warnings on platforms where * the Netlink interface isn't supported. */ static int -destroy_gre_netlink(struct netdev_obj_linux *netdev_obj UNUSED) +destroy_gre_netlink(struct netdev_dev_linux *netdev_dev UNUSED) { #ifdef GRE_IOCTL_ONLY return EOPNOTSUPP; #else - const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj); + const char *name = netdev_dev_get_name(&netdev_dev->netdev_dev); int error; struct ofpbuf request, *reply; struct ifinfomsg ifinfomsg; @@ -630,9 +629,9 @@ error: } static int -destroy_gre_ioctl(struct netdev_obj_linux *netdev_obj) +destroy_gre_ioctl(struct netdev_dev_linux *netdev_dev) { - const char *name = netdev_obj_get_name(&netdev_obj->netdev_obj); + const char *name = netdev_dev_get_name(&netdev_dev->netdev_dev); struct ip_tunnel_parm p; struct ifreq ifr; @@ -651,35 +650,44 @@ destroy_gre_ioctl(struct netdev_obj_linux *netdev_obj) } static void -destroy_tap(struct netdev_obj_linux *netdev_obj) +destroy_tap(struct netdev_dev_linux *netdev_dev) { - struct tap_state *state = &netdev_obj->state.tap; - if (state->tap_fd >= 0) { - close(state->tap_fd); + struct tap_state *state = &netdev_dev->state.tap; + + if (state->fd >= 0) { + close(state->fd); } } -/* Destroys the netdev object 'netdev_obj_'. */ +/* Destroys the netdev device 'netdev_dev_'. */ static void -netdev_linux_destroy(struct netdev_obj *netdev_obj_) +netdev_linux_destroy(struct netdev_dev *netdev_dev_) { - struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_); - const char *type = netdev_obj_get_type(netdev_obj_); + struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_); + const char *type = netdev_dev_get_type(netdev_dev_); - if (!strcmp(type, "tap")) { - destroy_tap(netdev_obj); + if (!strcmp(type, "system")) { + shash_delete(&cache_map, netdev_dev->shash_node); + + if (shash_is_empty(&cache_map)) { + rtnetlink_notifier_unregister(&netdev_linux_cache_notifier); + } + } else if (!strcmp(type, "tap")) { + destroy_tap(netdev_dev); } else if (!strcmp(type, "gre")) { if (gre_descriptors.use_ioctl) { - destroy_gre_ioctl(netdev_obj); + destroy_gre_ioctl(netdev_dev); } else { - destroy_gre_netlink(netdev_obj); + destroy_gre_netlink(netdev_dev); } } - free(netdev_obj); + + free(netdev_dev_); } static int -netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp) +netdev_linux_open(struct netdev_dev *netdev_dev, int ethertype, + struct netdev **netdevp) { struct netdev_linux *netdev; enum netdev_flags flags; @@ -687,28 +695,11 @@ netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp) /* Allocate network device. */ netdev = xzalloc(sizeof *netdev); - netdev_init(&netdev->netdev, name, &netdev_linux_class); + netdev_init(&netdev->netdev, netdev_dev); netdev->netdev_fd = -1; netdev->tap_fd = -1; - netdev->cache = shash_find_data(&cache_map, name); - if (!netdev->cache) { - if (shash_is_empty(&cache_map)) { - int error = rtnetlink_notifier_register( - &netdev_linux_cache_notifier, netdev_linux_cache_cb, NULL); - if (error) { - netdev_close(&netdev->netdev); - return error; - } - } - netdev->cache = xmalloc(sizeof *netdev->cache); - netdev->cache->shash_node = shash_add(&cache_map, name, - netdev->cache); - netdev->cache->valid = 0; - netdev->cache->ref_cnt = 0; - } - netdev->cache->ref_cnt++; - if (!strcmp(netdev_get_type(&netdev->netdev), "tap")) { + if (!strcmp(netdev_dev_get_type(netdev_dev), "tap")) { static const char tap_dev[] = "/dev/net/tun"; struct ifreq ifr; @@ -722,9 +713,11 @@ netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp) /* Create tap device. */ ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + strncpy(ifr.ifr_name, netdev_dev_get_name(netdev_dev), + sizeof ifr.ifr_name); if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) { - VLOG_WARN("%s: creating tap device failed: %s", name, + VLOG_WARN("%s: creating tap device failed: %s", + netdev_dev_get_name(netdev_dev), strerror(errno)); error = errno; goto error; @@ -779,7 +772,8 @@ netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp) if (bind(netdev->netdev_fd, (struct sockaddr *) &sll, sizeof sll) < 0) { error = errno; - VLOG_ERR("bind to %s failed: %s", name, strerror(error)); + VLOG_ERR("bind to %s failed: %s", netdev_dev_get_name(netdev_dev), + strerror(error)); goto error; } @@ -797,7 +791,7 @@ netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp) return 0; error: - netdev_close(&netdev->netdev); + netdev_uninit(&netdev->netdev, true); return error; } @@ -807,14 +801,6 @@ netdev_linux_close(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->cache && !--netdev->cache->ref_cnt) { - shash_delete(&cache_map, netdev->cache->shash_node); - free(netdev->cache); - - if (shash_is_empty(&cache_map)) { - rtnetlink_notifier_unregister(&netdev_linux_cache_notifier); - } - } if (netdev->netdev_fd >= 0) { close(netdev->netdev_fd); } @@ -890,7 +876,7 @@ netdev_linux_drain(struct netdev *netdev_) return 0; } else if (netdev->tap_fd != netdev->netdev_fd) { struct ifreq ifr; - int error = netdev_linux_do_ioctl(netdev_, &ifr, + int error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); if (error) { return error; @@ -974,15 +960,16 @@ static int netdev_linux_set_etheraddr(struct netdev *netdev_, const uint8_t mac[ETH_ADDR_LEN]) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); int error; - if (!(netdev->cache->valid & VALID_ETHERADDR) - || !eth_addr_equals(netdev->cache->etheraddr, mac)) { + if (!(netdev_dev->cache_valid & VALID_ETHERADDR) + || !eth_addr_equals(netdev_dev->etheraddr, mac)) { error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac); if (!error) { - netdev->cache->valid |= VALID_ETHERADDR; - memcpy(netdev->cache->etheraddr, mac, ETH_ADDR_LEN); + netdev_dev->cache_valid |= VALID_ETHERADDR; + memcpy(netdev_dev->etheraddr, mac, ETH_ADDR_LEN); } } else { error = 0; @@ -996,16 +983,17 @@ static int netdev_linux_get_etheraddr(const struct netdev *netdev_, uint8_t mac[ETH_ADDR_LEN]) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (!(netdev->cache->valid & VALID_ETHERADDR)) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + if (!(netdev_dev->cache_valid & VALID_ETHERADDR)) { int error = get_etheraddr(netdev_get_name(netdev_), - netdev->cache->etheraddr); + netdev_dev->etheraddr); if (error) { return error; } - netdev->cache->valid |= VALID_ETHERADDR; + netdev_dev->cache_valid |= VALID_ETHERADDR; } - memcpy(mac, netdev->cache->etheraddr, ETH_ADDR_LEN); + memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN); return 0; } @@ -1015,19 +1003,21 @@ netdev_linux_get_etheraddr(const struct netdev *netdev_, static int netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (!(netdev->cache->valid & VALID_MTU)) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + if (!(netdev_dev->cache_valid & VALID_MTU)) { struct ifreq ifr; int error; - error = netdev_linux_do_ioctl(netdev_, &ifr, SIOCGIFMTU, "SIOCGIFMTU"); + error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, + SIOCGIFMTU, "SIOCGIFMTU"); if (error) { return error; } - netdev->cache->mtu = ifr.ifr_mtu; - netdev->cache->valid |= VALID_MTU; + netdev_dev->mtu = ifr.ifr_mtu; + netdev_dev->cache_valid |= VALID_MTU; } - *mtup = netdev->cache->mtu; + *mtup = netdev_dev->mtu; return 0; } @@ -1045,16 +1035,18 @@ netdev_linux_get_ifindex(const struct netdev *netdev) static int netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + 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->cache->valid & VALID_CARRIER)) { + if (!(netdev_dev->cache_valid & VALID_CARRIER)) { char line[8]; int retval; - fn = xasprintf("/sys/class/net/%s/carrier", netdev_get_name(netdev_)); + fn = xasprintf("/sys/class/net/%s/carrier", + netdev_get_name(netdev_)); fd = open(fn, O_RDONLY); if (fd < 0) { error = errno; @@ -1084,10 +1076,10 @@ netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) fn, line[0]); goto exit; } - netdev->cache->carrier = line[0] != '0'; - netdev->cache->valid |= VALID_CARRIER; + netdev_dev->carrier = line[0] != '0'; + netdev_dev->cache_valid |= VALID_CARRIER; } - *carrier = netdev->cache->carrier; + *carrier = netdev_dev->carrier; error = 0; exit: @@ -1131,9 +1123,11 @@ check_for_working_netlink_stats(void) * XXX All of the members of struct netdev_stats are 64 bits wide, but on * 32-bit architectures the Linux network stats are only 32 bits. */ static int -netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) +netdev_linux_get_stats(const struct netdev *netdev_, + struct netdev_stats *stats) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); static int use_netlink_stats = -1; int error; struct netdev_stats raw_stats; @@ -1141,28 +1135,29 @@ netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) COVERAGE_INC(netdev_get_stats); - if (!(netdev->cache->valid & VALID_IS_INTERNAL)) { - netdev->cache->is_internal = (netdev->tap_fd != -1); + if (!(netdev_dev->cache_valid & VALID_IS_INTERNAL)) { + netdev_dev->is_internal = !strcmp(netdev_get_type(netdev_), + "tap"); - if (!netdev->cache->is_internal) { + if (!netdev_dev->is_internal) { struct ethtool_drvinfo drvinfo; memset(&drvinfo, 0, sizeof drvinfo); - error = netdev_linux_do_ethtool(&netdev->netdev, + error = netdev_linux_do_ethtool(netdev_, (struct ethtool_cmd *)&drvinfo, ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"); if (!error) { - netdev->cache->is_internal = !strcmp(drvinfo.driver, - "openvswitch"); + netdev_dev->is_internal = !strcmp(drvinfo.driver, + "openvswitch"); } } - netdev->cache->valid |= VALID_IS_INTERNAL; + netdev_dev->cache_valid |= VALID_IS_INTERNAL; } - if (netdev->cache->is_internal) { + if (netdev_dev->is_internal) { collect_stats = &raw_stats; } @@ -1172,19 +1167,19 @@ netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) if (use_netlink_stats) { int ifindex; - error = get_ifindex(&netdev->netdev, &ifindex); + error = get_ifindex(netdev_, &ifindex); if (!error) { error = get_stats_via_netlink(ifindex, collect_stats); } } else { - error = get_stats_via_proc(netdev->netdev.name, collect_stats); + error = get_stats_via_proc(netdev_get_name(netdev_), collect_stats); } /* If this port is an internal port then the transmit and receive stats * will appear to be swapped relative to the other ports since we are the * one sending the data, not a remote computer. For consistency, we swap * them back here. */ - if (netdev->cache->is_internal) { + if (netdev_dev->is_internal) { stats->rx_packets = raw_stats.tx_packets; stats->tx_packets = raw_stats.rx_packets; stats->rx_bytes = raw_stats.tx_bytes; @@ -1504,26 +1499,28 @@ static int netdev_linux_get_in4(const struct netdev *netdev_, struct in_addr *address, struct in_addr *netmask) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (!(netdev->cache->valid & VALID_IN4)) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + + if (!(netdev_dev->cache_valid & VALID_IN4)) { int error; - error = netdev_linux_get_ipv4(netdev_, &netdev->cache->address, + error = netdev_linux_get_ipv4(netdev_, &netdev_dev->address, SIOCGIFADDR, "SIOCGIFADDR"); if (error) { return error; } - error = netdev_linux_get_ipv4(netdev_, &netdev->cache->netmask, + error = netdev_linux_get_ipv4(netdev_, &netdev_dev->netmask, SIOCGIFNETMASK, "SIOCGIFNETMASK"); if (error) { return error; } - netdev->cache->valid |= VALID_IN4; + netdev_dev->cache_valid |= VALID_IN4; } - *address = netdev->cache->address; - *netmask = netdev->cache->netmask; + *address = netdev_dev->address; + *netmask = netdev_dev->netmask; return address->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0; } @@ -1531,14 +1528,15 @@ static int netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address, struct in_addr netmask) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); int error; error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address); if (!error) { - netdev->cache->valid |= VALID_IN4; - netdev->cache->address = address; - netdev->cache->netmask = netmask; + netdev_dev->cache_valid |= VALID_IN4; + netdev_dev->address = address; + netdev_dev->netmask = netmask; if (address.s_addr != INADDR_ANY) { error = do_set_addr(netdev_, SIOCSIFNETMASK, "SIOCSIFNETMASK", netmask); @@ -1568,12 +1566,13 @@ parse_if_inet6_line(const char *line, static int netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (!(netdev->cache->valid & VALID_IN6)) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + if (!(netdev_dev->cache_valid & VALID_IN6)) { FILE *file; char line[128]; - netdev->cache->in6 = in6addr_any; + netdev_dev->in6 = in6addr_any; file = fopen("/proc/net/if_inet6", "r"); if (file != NULL) { @@ -1584,15 +1583,15 @@ netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6) if (parse_if_inet6_line(line, &in6, ifname) && !strcmp(name, ifname)) { - netdev->cache->in6 = in6; + netdev_dev->in6 = in6; break; } } fclose(file); } - netdev->cache->valid |= VALID_IN6; + netdev_dev->cache_valid |= VALID_IN6; } - *in6 = netdev->cache->in6; + *in6 = netdev_dev->in6; return 0; } @@ -1614,9 +1613,11 @@ do_set_addr(struct netdev *netdev, int ioctl_nr, const char *ioctl_name, struct in_addr addr) { struct ifreq ifr; - strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); + strncpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name); make_in4_sockaddr(&ifr.ifr_addr, addr); - return netdev_linux_do_ioctl(netdev, &ifr, ioctl_nr, ioctl_name); + + return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr, + ioctl_name); } /* Adds 'router' as a default IP gateway. */ @@ -1720,14 +1721,14 @@ netdev_linux_arp_lookup(const struct netdev *netdev, memcpy(&r.arp_pa, &sin, sizeof sin); r.arp_ha.sa_family = ARPHRD_ETHER; r.arp_flags = 0; - strncpy(r.arp_dev, netdev->name, sizeof r.arp_dev); + strncpy(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; if (!retval) { memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN); } else if (retval != ENXIO) { VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s", - netdev->name, IP_ARGS(&ip), strerror(retval)); + netdev_get_name(netdev), IP_ARGS(&ip), strerror(retval)); } return retval; } @@ -1860,7 +1861,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_) } const struct netdev_class netdev_linux_class = { - "system", /* type */ + "system", netdev_linux_init, netdev_linux_run, @@ -1908,11 +1909,11 @@ const struct netdev_class netdev_linux_class = { }; const struct netdev_class netdev_tap_class = { - "tap", /* type */ + "tap", netdev_linux_init, - NULL, /* run */ - NULL, /* wait */ + netdev_linux_run, + netdev_linux_wait, netdev_linux_create_tap, netdev_linux_destroy, @@ -1921,7 +1922,7 @@ const struct netdev_class netdev_tap_class = { netdev_linux_open, netdev_linux_close, - netdev_linux_enumerate, + NULL, /* enumerate */ netdev_linux_recv, netdev_linux_recv_wait, @@ -1956,11 +1957,11 @@ const struct netdev_class netdev_tap_class = { }; const struct netdev_class netdev_gre_class = { - "gre", /* type */ + "gre", netdev_linux_init, - NULL, /* run */ - NULL, /* wait */ + netdev_linux_run, + netdev_linux_wait, netdev_linux_create_gre, netdev_linux_destroy, @@ -1969,7 +1970,7 @@ const struct netdev_class netdev_gre_class = { netdev_linux_open, netdev_linux_close, - netdev_linux_enumerate, + NULL, /* enumerate */ netdev_linux_recv, netdev_linux_recv_wait, @@ -2150,7 +2151,8 @@ get_flags(const struct netdev *netdev, int *flags) struct ifreq ifr; int error; - error = netdev_linux_do_ioctl(netdev, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS"); + error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCGIFFLAGS, + "SIOCGIFFLAGS"); *flags = ifr.ifr_flags; return error; } @@ -2161,7 +2163,8 @@ set_flags(struct netdev *netdev, int flags) struct ifreq ifr; ifr.ifr_flags = flags; - return netdev_linux_do_ioctl(netdev, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); + return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCSIFFLAGS, + "SIOCSIFFLAGS"); } static int @@ -2182,17 +2185,18 @@ do_get_ifindex(const char *netdev_name) static int get_ifindex(const struct netdev *netdev_, int *ifindexp) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); *ifindexp = 0; - if (!(netdev->cache->valid & VALID_IFINDEX)) { + if (!(netdev_dev->cache_valid & VALID_IFINDEX)) { int ifindex = do_get_ifindex(netdev_get_name(netdev_)); if (ifindex < 0) { return -ifindex; } - netdev->cache->valid |= VALID_IFINDEX; - netdev->cache->ifindex = ifindex; + netdev_dev->cache_valid |= VALID_IFINDEX; + netdev_dev->ifindex = ifindex; } - *ifindexp = netdev->cache->ifindex; + *ifindexp = netdev_dev->ifindex; return 0; } @@ -2239,13 +2243,13 @@ set_etheraddr(const char *netdev_name, int hwaddr_family, } static int -netdev_linux_do_ethtool(struct netdev *netdev, struct ethtool_cmd *ecmd, +netdev_linux_do_ethtool(const struct netdev *netdev, struct ethtool_cmd *ecmd, int cmd, const char *cmd_name) { struct ifreq ifr; memset(&ifr, 0, sizeof ifr); - strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); + strncpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name); ifr.ifr_data = (caddr_t) ecmd; ecmd->cmd = cmd; @@ -2255,7 +2259,7 @@ netdev_linux_do_ethtool(struct netdev *netdev, struct ethtool_cmd *ecmd, } else { if (errno != EOPNOTSUPP) { VLOG_WARN_RL(&rl, "ethtool command %s on network device %s " - "failed: %s", cmd_name, netdev->name, + "failed: %s", cmd_name, netdev_get_name(netdev), strerror(errno)); } else { /* The device doesn't support this operation. That's pretty @@ -2266,13 +2270,13 @@ netdev_linux_do_ethtool(struct netdev *netdev, struct ethtool_cmd *ecmd, } static int -netdev_linux_do_ioctl(const struct netdev *netdev, struct ifreq *ifr, - int cmd, const char *cmd_name) +netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd, + const char *cmd_name) { - strncpy(ifr->ifr_name, netdev_get_name(netdev), sizeof ifr->ifr_name); + strncpy(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", - netdev_get_name(netdev), cmd_name, strerror(errno)); + VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name, + strerror(errno)); return errno; } return 0; @@ -2286,7 +2290,7 @@ 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, &ifr, cmd, cmd_name); + error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name); if (!error) { const struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; *ip = sin->sin_addr; diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h index 71f9599e..4148c6cf 100644 --- a/lib/netdev-provider.h +++ b/lib/netdev-provider.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Nicira Networks. + * Copyright (c) 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,53 +20,56 @@ /* Generic interface to network devices. */ #include + #include "netdev.h" #include "list.h" #include "shash.h" -/* A network device object that was created through the netdev_create() - * call. +/* A network device (e.g. an Ethernet device). * * This structure should be treated as opaque by network device * implementations. */ -struct netdev_obj { - const struct netdev_class *class; - int ref_cnt; - char *name; - bool created; /* Was netdev_create() called? */ +struct netdev_dev { + char *name; /* Name of network device. */ + const struct netdev_class *class; /* Functions to control this device. */ + int ref_cnt; /* Times this devices was opened. */ + struct shash_node *node; /* Pointer to element in global map. */ + uint32_t args_hash; /* Hash of arguments for the device. */ }; -void netdev_obj_init(struct netdev_obj *, const char *name, - const struct netdev_class *, bool created); -static inline void netdev_obj_assert_class(const struct netdev_obj *netdev_obj, +void netdev_dev_init(struct netdev_dev *, const char *name, + const struct netdev_class *); +void netdev_dev_uninit(struct netdev_dev *, bool destroy); +const char *netdev_dev_get_type(const struct netdev_dev *); +const char *netdev_dev_get_name(const struct netdev_dev *); + +static inline void netdev_dev_assert_class(const struct netdev_dev *netdev_dev, const struct netdev_class *class) { - assert(netdev_obj->class == class); + assert(netdev_dev->class == class); } -const char *netdev_obj_get_type(const struct netdev_obj *netdev_obj); -const char *netdev_obj_get_name(const struct netdev_obj *netdev_obj); -/* A network device (e.g. an Ethernet device). +/* A instance of an open network device. * * This structure should be treated as opaque by network device * implementations. */ struct netdev { - const struct netdev_class *class; - char *name; /* e.g. "eth0" */ + struct netdev_dev *netdev_dev; /* Parent netdev_dev. */ + struct list node; /* Element in global list. */ enum netdev_flags save_flags; /* Initial device flags. */ enum netdev_flags changed_flags; /* Flags that we changed. */ - struct list node; /* Element in global list. */ }; -void netdev_init(struct netdev *, const char *name, - const struct netdev_class *); +void netdev_init(struct netdev *, struct netdev_dev *); +void netdev_uninit(struct netdev *, bool close); +struct netdev_dev *netdev_get_dev(const struct netdev *); + static inline void netdev_assert_class(const struct netdev *netdev, const struct netdev_class *class) { - assert(netdev->class == class); + netdev_dev_assert_class(netdev_get_dev(netdev), class); } -const char *netdev_get_type(const struct netdev *netdev); /* A network device notifier. * @@ -90,9 +93,9 @@ struct netdev_class { /* Type of netdevs in this class, e.g. "system", "tap", "gre", etc. * * One of the providers should supply a "system" type, since this is - * the type assumed when a device name was not bound through the - * netdev_create() call. The "system" type corresponds to an - * existing network device on the system. */ + * the type assumed if no type is specified when opening a netdev. + * The "system" type corresponds to an existing network device on + * the system. */ const char *type; /* Called only once, at program startup. Returning an error from this @@ -111,45 +114,36 @@ struct netdev_class { * to be called. May be null if nothing is needed here. */ void (*wait)(void); - /* Attempts to create a network device object of 'type' with 'name'. + /* Attempts to create a network device of 'type' with 'name'. * 'type' corresponds to the 'type' field used in the netdev_class - * structure. - * - * The 'created' flag indicates that the user called netdev_create() - * and thus will eventually call netdev_destroy(). If the flag is - * false, then the object was dynamically created based on a call to - * netdev_open() without first calling netdev_create() and will be - * automatically destroyed when no more netdevs have 'name' open. A - * provider implementation should pass this flag to netdev_obj_init(). */ - int (*create)(const char *name, const char *type, - const struct shash *args, bool created); - - /* Destroys 'netdev_obj'. + * structure. On success sets 'netdev_devp' to the newly created device. */ + int (*create)(const char *name, const char *type, const struct shash *args, + struct netdev_dev **netdev_devp); + + /* Destroys 'netdev_dev'. * - * Netdev objects maintain a reference count that is incremented on - * netdev_open() and decremented on netdev_close(). If 'netdev_obj' - * has a non-zero reference count, then this function will not be + * Netdev devices maintain a reference count that is incremented on + * netdev_open() and decremented on netdev_close(). If 'netdev_dev' + * has a non-zero reference count, then this function will not be * called. */ - void (*destroy)(struct netdev_obj *netdev_obj); + void (*destroy)(struct netdev_dev *netdev_dev); - /* Reconfigures the device object 'netdev_obj' with 'args'. + /* Reconfigures the device 'netdev_dev' with 'args'. * * If this netdev class does not support reconfiguring a netdev - * object, this may be a null pointer. + * device, this may be a null pointer. */ - int (*reconfigure)(struct netdev_obj *netdev_obj, - const struct shash *args); + int (*reconfigure)(struct netdev_dev *netdev_dev, const struct shash *args); - /* Attempts to open a network device. On success, sets '*netdevp' to the - * new network device. 'name' is the network device name provided by - * the user. This name is useful for error messages but must not be - * modified. + /* Attempts to open a network device. On success, sets 'netdevp' + * to the new network device. * * 'ethertype' may be a 16-bit Ethernet protocol value in host byte order * to capture frames of that type received on the device. It may also be * one of the 'enum netdev_pseudo_ethertype' values to receive frames in * one of those categories. */ - int (*open)(const char *name, int ethertype, struct netdev **netdevp); + int (*open)(struct netdev_dev *netdev_dev, int ethertype, + struct netdev **netdevp); /* Closes 'netdev'. */ void (*close)(struct netdev *netdev); @@ -211,7 +205,7 @@ struct netdev_class { * The MTU is the maximum size of transmitted (and received) packets, in * bytes, not including the hardware header; thus, this is typically 1500 * bytes for Ethernet devices.*/ - int (*get_mtu)(const struct netdev *, int *mtup); + int (*get_mtu)(const struct netdev *netdev, int *mtup); /* Returns the ifindex of 'netdev', if successful, as a positive number. * On failure, returns a negative errno value. @@ -221,7 +215,7 @@ struct netdev_class { * ifindex value should be unique within a host and remain stable at least * until reboot. SNMP says an ifindex "ranges between 1 and the value of * ifNumber" but many systems do not follow this rule anyhow. */ - int (*get_ifindex)(const struct netdev *); + int (*get_ifindex)(const struct netdev *netdev); /* Sets 'carrier' to true if carrier is active (link light is on) on * 'netdev'. */ @@ -232,7 +226,7 @@ struct netdev_class { * A network device that supports some statistics but not others, it should * set the values of the unsupported statistics to all-1-bits * (UINT64_MAX). */ - int (*get_stats)(const struct netdev *netdev, struct netdev_stats *stats); + int (*get_stats)(const struct netdev *netdev, struct netdev_stats *); /* Stores the features supported by 'netdev' into each of '*current', * '*advertised', '*supported', and '*peer'. Each value is a bitmap of @@ -246,14 +240,14 @@ struct netdev_class { * * This function may be set to null for a network device that does not * support configuring advertisements. */ - int (*set_advertisements)(struct netdev *, uint32_t advertise); + int (*set_advertisements)(struct netdev *netdev, uint32_t advertise); /* If 'netdev' is 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. * - * Returns ENOENT if 'netdev_name' is the name of a network device that is - * not a VLAN device. + * Returns ENOENT if 'netdev' is a network device that is not a + * VLAN device. * * This function should be set to null if it doesn't make any sense for * your network device (it probably doesn't). */ @@ -286,7 +280,8 @@ struct netdev_class { * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ - int (*set_in4)(struct netdev *, struct in_addr addr, struct in_addr mask); + int (*set_in4)(struct netdev *netdev, struct in_addr addr, + struct in_addr mask); /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address. * @@ -325,12 +320,12 @@ struct netdev_class { * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ - int (*arp_lookup)(const struct netdev *, uint32_t ip, uint8_t mac[6]); + int (*arp_lookup)(const struct netdev *netdev, uint32_t ip, uint8_t mac[6]); - /* Retrieves the current set of flags on 'netdev' into '*old_flags'. Then, - * turns off the flags that are set to 1 in 'off' and turns on the flags - * that are set to 1 in 'on'. (No bit will be set to 1 in both 'off' and - * 'on'; that is, off & on == 0.) + /* Retrieves the current set of flags on 'netdev' into '*old_flags'. + * Then, turns off the flags that are set to 1 in 'off' and turns on the + * flags that are set to 1 in 'on'. (No bit will be set to 1 in both 'off' + * and 'on'; that is, off & on == 0.) * * This function may be invoked from a signal handler. Therefore, it * should not do anything that is not signal-safe (such as logging). */ @@ -343,7 +338,7 @@ struct netdev_class { * will have its 'netdev', 'cb', and 'aux' members set to the values of the * corresponding parameters. */ int (*poll_add)(struct netdev *netdev, - void (*cb)(struct netdev_notifier *), void *aux, + void (*cb)(struct netdev_notifier *notifier), void *aux, struct netdev_notifier **notifierp); /* Cancels poll notification for 'notifier'. */ diff --git a/lib/netdev.c b/lib/netdev.c index 77964f93..2a291091 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ #include "coverage.h" #include "dynamic-string.h" #include "fatal-signal.h" +#include "hash.h" #include "list.h" #include "netdev-provider.h" #include "ofpbuf.h" @@ -47,7 +48,7 @@ static const struct netdev_class *netdev_classes[] = { static int n_netdev_classes = ARRAY_SIZE(netdev_classes); /* All created network devices. */ -static struct shash netdev_obj_shash = SHASH_INITIALIZER(&netdev_obj_shash); +static struct shash netdev_dev_shash = SHASH_INITIALIZER(&netdev_dev_shash); /* All open network devices. */ static struct list netdev_list = LIST_INITIALIZER(&netdev_list); @@ -129,184 +130,179 @@ netdev_wait(void) } } -/* Attempts to create a network device object of 'type' with 'name'. 'type' - * corresponds to the 'type' field used in the netdev_class * structure. - * Arguments for creation are provided in 'args', which may be empty or NULL - * if none are needed. */ -int -netdev_create(const char *name, const char *type, const struct shash *args) +static int +create_device(struct netdev_options *options, struct netdev_dev **netdev_devp) { - struct shash empty_args = SHASH_INITIALIZER(&empty_args); int i; - netdev_initialize(); - - if (!args) { - args = &empty_args; + if (!options->may_create) { + VLOG_WARN("attempted to create a device that may not be created: %s", + options->name); + return ENODEV; } - if (shash_find(&netdev_obj_shash, name)) { - VLOG_WARN("attempted to create a netdev object with bound name: %s", - name); - return EEXIST; + if (!options->type || strlen(options->type) == 0) { + /* Default to system. */ + options->type = "system"; } for (i = 0; i < n_netdev_classes; i++) { const struct netdev_class *class = netdev_classes[i]; - if (!strcmp(type, class->type)) { - return class->create(name, type, args, true); + + if (!strcmp(options->type, class->type)) { + return class->create(options->name, options->type, options->args, + netdev_devp); } } - VLOG_WARN("could not create netdev object of unknown type: %s", type); - + VLOG_WARN("could not create netdev %s of unknown type %s", options->name, + options->type); return EINVAL; } -/* Destroys netdev object 'name'. Netdev objects maintain a reference count - * which is incremented on netdev_open() and decremented on netdev_close(). - * If 'name' has a non-zero reference count, it will not destroy the object - * and return EBUSY. */ -int -netdev_destroy(const char *name) +static uint32_t +shash_hash(const struct shash *shash) { + int hash = 0; struct shash_node *node; - struct netdev_obj *netdev_obj; - - node = shash_find(&netdev_obj_shash, name); - if (!node) { - return ENODEV; - } - - netdev_obj = node->data; - if (netdev_obj->ref_cnt != 0) { - VLOG_WARN("attempt to destroy netdev object with %d open handles: %s", - netdev_obj->ref_cnt, name); -#if 0 /* Temp hack */ - return EBUSY; -#endif - } - - shash_delete(&netdev_obj_shash, node); - netdev_obj->class->destroy(netdev_obj); - - return 0; -} - -/* Reconfigures the device object 'name' with 'args'. 'args' may be empty - * or NULL if none are needed. */ -int -netdev_reconfigure(const char *name, const struct shash *args) -{ - struct shash empty_args = SHASH_INITIALIZER(&empty_args); - struct netdev_obj *netdev_obj; - - if (!args) { - args = &empty_args; - } + uint32_t entry_hash; - netdev_obj = shash_find_data(&netdev_obj_shash, name); - if (!netdev_obj) { - return ENODEV; + SHASH_FOR_EACH(node, shash) { + entry_hash = hash_string(node->name, 0); + entry_hash ^= hash_string(node->data, 10); + hash ^= hash_int(entry_hash, 0); } - if (netdev_obj->class->reconfigure) { - return netdev_obj->class->reconfigure(netdev_obj, args); - } - - return 0; + return hash; } /* Opens the network device named 'name' (e.g. "eth0") and returns zero if * successful, otherwise a positive errno value. On success, sets '*netdevp' * to the new network device, otherwise to null. * + * If this is the first time the device has been opened, then create is called + * before opening. The device is created using the given type and arguments. + * * 'ethertype' may be a 16-bit Ethernet protocol value in host byte order to * capture frames of that type received on the device. It may also be one of * the 'enum netdev_pseudo_ethertype' values to receive frames in one of those - * categories. */ + * categories. + * + * If the 'may_create' flag is set then this is allowed to be the first time + * the device is opened (i.e. the refcount will be 1 after this call). It + * may be set to false if the device should have already been created. + * + * If the 'may_open' flag is set then the call will succeed even if another + * caller has already opened it. It may be to false if the device should not + * currently be open. */ + int -netdev_open(const char *name, int ethertype, struct netdev **netdevp) +netdev_open(struct netdev_options *options, struct netdev **netdevp) { - struct netdev_obj *netdev_obj; - struct netdev *netdev = NULL; + struct shash empty_args = SHASH_INITIALIZER(&empty_args); + struct netdev_dev *netdev_dev; int error; - int i; + *netdevp = NULL; netdev_initialize(); - netdev_obj = shash_find_data(&netdev_obj_shash, name); - if (netdev_obj) { - error = netdev_obj->class->open(name, ethertype, &netdev); - } else { - /* Default to "system". */ - error = EAFNOSUPPORT; - for (i = 0; i < n_netdev_classes; i++) { - const struct netdev_class *class = netdev_classes[i]; - if (!strcmp(class->type, "system")) { - struct shash empty_args = SHASH_INITIALIZER(&empty_args); + if (!options->args) { + options->args = &empty_args; + } - /* Dynamically create the netdev object, but indicate - * that it should be destroyed when the the last user - * closes its handle. */ - error = class->create(name, "system", &empty_args, false); - if (!error) { - error = class->open(name, ethertype, &netdev); - netdev_obj = shash_find_data(&netdev_obj_shash, name); - } - break; + netdev_dev = shash_find_data(&netdev_dev_shash, options->name); + + if (!netdev_dev) { + error = create_device(options, &netdev_dev); + if (error) { + return error; + } + + netdev_dev->args_hash = shash_hash(options->args); + + } else if (options->may_open) { + if (!shash_is_empty(options->args)) { + uint32_t args_hash = shash_hash(options->args); + + if (args_hash != netdev_dev->args_hash) { + VLOG_WARN("attempted to open already created netdev with " + "different arguments: %s", options->name); + return EINVAL; } } + } else { + VLOG_WARN("attempted to create a netdev device with bound name: %s", + options->name); + return EEXIST; } + + error = netdev_dev->class->open(netdev_dev, options->ethertype, netdevp); + if (!error) { - netdev_obj->ref_cnt++; + netdev_dev->ref_cnt++; + } else { + if (!netdev_dev->ref_cnt) { + netdev_dev_uninit(netdev_dev, true); + } } - *netdevp = error ? NULL : netdev; return error; } +int +netdev_open_default(const char *name, struct netdev **netdevp) +{ + struct netdev_options options; + + memset(&options, 0, sizeof options); + + options.name = name; + options.ethertype = NETDEV_ETH_TYPE_NONE; + options.may_create = true; + options.may_open = true; + + return netdev_open(&options, netdevp); +} + +/* Reconfigures the device 'netdev' with 'args'. 'args' may be empty + * or NULL if none are needed. */ +int +netdev_reconfigure(struct netdev *netdev, const struct shash *args) +{ + struct shash empty_args = SHASH_INITIALIZER(&empty_args); + struct netdev_dev *netdev_dev = netdev_get_dev(netdev); + + if (!args) { + args = &empty_args; + } + + if (netdev_dev->class->reconfigure) { + uint32_t args_hash = shash_hash(args); + + if (netdev_dev->args_hash != args_hash) { + netdev_dev->args_hash = args_hash; + return netdev_dev->class->reconfigure(netdev_dev, args); + } + } + + return 0; +} + /* Closes and destroys 'netdev'. */ void netdev_close(struct netdev *netdev) { if (netdev) { - struct netdev_obj *netdev_obj; - char *name = netdev->name; - int error; - - netdev_obj = shash_find_data(&netdev_obj_shash, name); -#if 0 - assert(netdev_obj); -#else - if (netdev_obj) { -#endif - if (netdev_obj->ref_cnt > 0) { - netdev_obj->ref_cnt--; - } else { - VLOG_WARN("netdev %s closed too many times", name); - } + struct netdev_dev *netdev_dev = netdev_get_dev(netdev); - /* If the reference count for the netdev object is zero, and it - * was dynamically created by netdev_open(), destroy it. */ - if (!netdev_obj->ref_cnt && !netdev_obj->created) { - netdev_destroy(name); - } -#if 1 - } -#endif + assert(netdev_dev->ref_cnt); + netdev_dev->ref_cnt--; + netdev_uninit(netdev, true); - /* Restore flags that we changed, if any. */ - error = restore_flags(netdev); - list_remove(&netdev->node); - if (error) { - VLOG_WARN("failed to restore network device flags on %s: %s", - name, strerror(error)); + /* If the reference count for the netdev device is zero, destroy it. */ + if (!netdev_dev->ref_cnt) { + netdev_dev_uninit(netdev_dev, true); } - - /* Free. */ - netdev->class->close(netdev); - free(name); } } @@ -318,7 +314,7 @@ netdev_exists(const char *name) struct netdev *netdev; int error; - error = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev); + error = netdev_open_default(name, &netdev); if (!error) { netdev_close(netdev); return true; @@ -379,8 +375,8 @@ netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) assert(buffer->size == 0); assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN); - retval = netdev->class->recv(netdev, - buffer->data, ofpbuf_tailroom(buffer)); + retval = netdev_get_dev(netdev)->class->recv(netdev, buffer->data, + ofpbuf_tailroom(buffer)); if (retval >= 0) { COVERAGE_INC(netdev_received); buffer->size += retval; @@ -398,14 +394,14 @@ netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) void netdev_recv_wait(struct netdev *netdev) { - netdev->class->recv_wait(netdev); + netdev_get_dev(netdev)->class->recv_wait(netdev); } /* Discards all packets waiting to be received from 'netdev'. */ int netdev_drain(struct netdev *netdev) { - return netdev->class->drain(netdev); + return netdev_get_dev(netdev)->class->drain(netdev); } /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive @@ -420,7 +416,8 @@ netdev_drain(struct netdev *netdev) int netdev_send(struct netdev *netdev, const struct ofpbuf *buffer) { - int error = netdev->class->send(netdev, buffer->data, buffer->size); + int error = netdev_get_dev(netdev)->class->send(netdev, buffer->data, + buffer->size); if (!error) { COVERAGE_INC(netdev_sent); } @@ -437,7 +434,7 @@ netdev_send(struct netdev *netdev, const struct ofpbuf *buffer) void netdev_send_wait(struct netdev *netdev) { - return netdev->class->send_wait(netdev); + return netdev_get_dev(netdev)->class->send_wait(netdev); } /* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, @@ -445,7 +442,7 @@ netdev_send_wait(struct netdev *netdev) int netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN]) { - return netdev->class->set_etheraddr(netdev, mac); + return netdev_get_dev(netdev)->class->set_etheraddr(netdev, mac); } /* Retrieves 'netdev''s MAC address. If successful, returns 0 and copies the @@ -454,7 +451,7 @@ netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN]) int netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN]) { - return netdev->class->get_etheraddr(netdev, mac); + return netdev_get_dev(netdev)->class->get_etheraddr(netdev, mac); } /* Returns the name of the network device that 'netdev' represents, @@ -462,7 +459,7 @@ netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN]) const char * netdev_get_name(const struct netdev *netdev) { - return netdev->name; + return netdev_get_dev(netdev)->name; } /* Retrieves the MTU of 'netdev'. The MTU is the maximum size of transmitted @@ -475,7 +472,7 @@ netdev_get_name(const struct netdev *netdev) int netdev_get_mtu(const struct netdev *netdev, int *mtup) { - int error = netdev->class->get_mtu(netdev, mtup); + int error = netdev_get_dev(netdev)->class->get_mtu(netdev, mtup); if (error) { VLOG_WARN_RL(&rl, "failed to retrieve MTU for network device %s: %s", netdev_get_name(netdev), strerror(error)); @@ -496,7 +493,7 @@ netdev_get_mtu(const struct netdev *netdev, int *mtup) int netdev_get_ifindex(const struct netdev *netdev) { - return netdev->class->get_ifindex(netdev); + return netdev_get_dev(netdev)->class->get_ifindex(netdev); } /* Stores the features supported by 'netdev' into each of '*current', @@ -525,8 +522,9 @@ netdev_get_features(struct netdev *netdev, peer = &dummy[3]; } - error = netdev->class->get_features(netdev, current, advertised, supported, - peer); + error = netdev_get_dev(netdev)->class->get_features(netdev, current, + advertised, supported, + peer); if (error) { *current = *advertised = *supported = *peer = 0; } @@ -538,8 +536,9 @@ netdev_get_features(struct netdev *netdev, int netdev_set_advertisements(struct netdev *netdev, uint32_t advertise) { - return (netdev->class->set_advertisements - ? netdev->class->set_advertisements(netdev, advertise) + return (netdev_get_dev(netdev)->class->set_advertisements + ? netdev_get_dev(netdev)->class->set_advertisements(netdev, + advertise) : EOPNOTSUPP); } @@ -563,8 +562,9 @@ netdev_get_in4(const struct netdev *netdev, struct in_addr netmask; int error; - error = (netdev->class->get_in4 - ? netdev->class->get_in4(netdev, &address, &netmask) + error = (netdev_get_dev(netdev)->class->get_in4 + ? netdev_get_dev(netdev)->class->get_in4(netdev, &address, + &netmask) : EOPNOTSUPP); if (address_) { address_->s_addr = error ? 0 : address.s_addr; @@ -581,8 +581,8 @@ netdev_get_in4(const struct netdev *netdev, int netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask) { - return (netdev->class->set_in4 - ? netdev->class->set_in4(netdev, addr, mask) + return (netdev_get_dev(netdev)->class->set_in4 + ? netdev_get_dev(netdev)->class->set_in4(netdev, addr, mask) : EOPNOTSUPP); } @@ -592,8 +592,8 @@ int netdev_add_router(struct netdev *netdev, struct in_addr router) { COVERAGE_INC(netdev_add_router); - return (netdev->class->add_router - ? netdev->class->add_router(netdev, router) + return (netdev_get_dev(netdev)->class->add_router + ? netdev_get_dev(netdev)->class->add_router(netdev, router) : EOPNOTSUPP); } @@ -609,8 +609,9 @@ netdev_get_next_hop(const struct netdev *netdev, const struct in_addr *host, struct in_addr *next_hop, char **netdev_name) { - int error = (netdev->class->get_next_hop - ? netdev->class->get_next_hop(host, next_hop, netdev_name) + int error = (netdev_get_dev(netdev)->class->get_next_hop + ? netdev_get_dev(netdev)->class->get_next_hop(host, next_hop, + netdev_name) : EOPNOTSUPP); if (error) { next_hop->s_addr = 0; @@ -636,8 +637,9 @@ netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6) struct in6_addr dummy; int error; - error = (netdev->class->get_in6 - ? netdev->class->get_in6(netdev, in6 ? in6 : &dummy) + error = (netdev_get_dev(netdev)->class->get_in6 + ? netdev_get_dev(netdev)->class->get_in6(netdev, in6 ? in6 + : &dummy) : EOPNOTSUPP); if (error && in6) { memset(in6, 0, sizeof *in6); @@ -657,7 +659,8 @@ do_update_flags(struct netdev *netdev, enum netdev_flags off, enum netdev_flags old_flags; int error; - error = netdev->class->update_flags(netdev, off & ~on, on, &old_flags); + error = netdev_get_dev(netdev)->class->update_flags(netdev, off & ~on, on, + &old_flags); if (error) { VLOG_WARN_RL(&rl, "failed to %s flags for network device %s: %s", off || on ? "set" : "get", netdev_get_name(netdev), @@ -730,8 +733,8 @@ int netdev_arp_lookup(const struct netdev *netdev, uint32_t ip, uint8_t mac[ETH_ADDR_LEN]) { - int error = (netdev->class->arp_lookup - ? netdev->class->arp_lookup(netdev, ip, mac) + int error = (netdev_get_dev(netdev)->class->arp_lookup + ? netdev_get_dev(netdev)->class->arp_lookup(netdev, ip, mac) : EOPNOTSUPP); if (error) { memset(mac, 0, ETH_ADDR_LEN); @@ -744,8 +747,8 @@ netdev_arp_lookup(const struct netdev *netdev, int netdev_get_carrier(const struct netdev *netdev, bool *carrier) { - int error = (netdev->class->get_carrier - ? netdev->class->get_carrier(netdev, carrier) + int error = (netdev_get_dev(netdev)->class->get_carrier + ? netdev_get_dev(netdev)->class->get_carrier(netdev, carrier) : EOPNOTSUPP); if (error) { *carrier = false; @@ -760,8 +763,8 @@ netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats) int error; COVERAGE_INC(netdev_get_stats); - error = (netdev->class->get_stats - ? netdev->class->get_stats(netdev, stats) + error = (netdev_get_dev(netdev)->class->get_stats + ? netdev_get_dev(netdev)->class->get_stats(netdev, stats) : EOPNOTSUPP); if (error) { memset(stats, 0xff, sizeof *stats); @@ -776,8 +779,9 @@ int netdev_set_policing(struct netdev *netdev, uint32_t kbits_rate, uint32_t kbits_burst) { - return (netdev->class->set_policing - ? netdev->class->set_policing(netdev, kbits_rate, kbits_burst) + return (netdev_get_dev(netdev)->class->set_policing + ? netdev_get_dev(netdev)->class->set_policing(netdev, kbits_rate, + kbits_burst) : EOPNOTSUPP); } @@ -789,8 +793,8 @@ netdev_set_policing(struct netdev *netdev, uint32_t kbits_rate, int netdev_get_vlan_vid(const struct netdev *netdev, int *vlan_vid) { - int error = (netdev->class->get_vlan_vid - ? netdev->class->get_vlan_vid(netdev, vlan_vid) + int error = (netdev_get_dev(netdev)->class->get_vlan_vid + ? netdev_get_dev(netdev)->class->get_vlan_vid(netdev, vlan_vid) : ENOENT); if (error) { *vlan_vid = 0; @@ -812,7 +816,7 @@ netdev_find_dev_by_in4(const struct in_addr *in4) const char *name = dev_list.names[i]; struct in_addr dev_in4; - if (!netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev) + if (!netdev_open_default(name, &netdev) && !netdev_get_in4(netdev, &dev_in4, NULL) && dev_in4.s_addr == in4->s_addr) { goto exit; @@ -826,62 +830,113 @@ exit: return netdev; } -/* Initializes 'netdev_obj' as a netdev object named 'name' of the +/* Initializes 'netdev_dev' as a netdev device named 'name' of the * specified 'class'. * - * This function adds 'netdev_obj' to a netdev-owned shash, so it is - * very important that 'netdev_obj' only be freed after calling - * netdev_destroy(). */ + * This function adds 'netdev_dev' to a netdev-owned shash, so it is + * very important that 'netdev_dev' only be freed after calling + * the refcount drops to zero. */ +void +netdev_dev_init(struct netdev_dev *netdev_dev, const char *name, + const struct netdev_class *class) +{ + assert(!shash_find(&netdev_dev_shash, name)); + + netdev_dev->class = class; + netdev_dev->ref_cnt = 0; + netdev_dev->name = xstrdup(name); + netdev_dev->node = shash_add(&netdev_dev_shash, name, netdev_dev); +} + +/* Undoes the results of initialization. + * + * Normally this function does not need to be called as netdev_close has + * the same effect when the refcount drops to zero. + * However, it may be called by providers due to an error on creation + * that occurs after initialization. It this case netdev_close() would + * never be called. */ void -netdev_obj_init(struct netdev_obj *netdev_obj, const char *name, - const struct netdev_class *class, bool created) +netdev_dev_uninit(struct netdev_dev *netdev_dev, bool destroy) { - assert(!shash_find(&netdev_obj_shash, name)); + char *name = netdev_dev->name; + + assert(!netdev_dev->ref_cnt); + + shash_delete(&netdev_dev_shash, netdev_dev->node); - netdev_obj->class = class; - netdev_obj->ref_cnt = 0; - netdev_obj->created = created; - netdev_obj->name = xstrdup(name); - shash_add(&netdev_obj_shash, name, netdev_obj); + if (destroy) { + netdev_dev->class->destroy(netdev_dev); + } + free(name); } -/* Returns the class type of 'netdev_obj'. +/* Returns the class type of 'netdev_dev'. * * The caller must not free the returned value. */ -const char *netdev_obj_get_type(const struct netdev_obj *netdev_obj) +const char * +netdev_dev_get_type(const struct netdev_dev *netdev_dev) { - return netdev_obj->class->type; + return netdev_dev->class->type; } -/* Returns the name of 'netdev_obj'. +/* Returns the name of 'netdev_dev'. * * The caller must not free the returned value. */ -const char *netdev_obj_get_name(const struct netdev_obj *netdev_obj) +const char * +netdev_dev_get_name(const struct netdev_dev *netdev_dev) { - return netdev_obj->name; + return netdev_dev->name; } -/* Initializes 'netdev' as a netdev named 'name' of the specified 'class'. +/* Initializes 'netdev' as a instance of the netdev_dev. * * This function adds 'netdev' to a netdev-owned linked list, so it is very * important that 'netdev' only be freed after calling netdev_close(). */ void -netdev_init(struct netdev *netdev, const char *name, - const struct netdev_class *class) +netdev_init(struct netdev *netdev, struct netdev_dev *netdev_dev) { - netdev->class = class; - netdev->name = xstrdup(name); + netdev->netdev_dev = netdev_dev; netdev->save_flags = 0; netdev->changed_flags = 0; list_push_back(&netdev_list, &netdev->node); } +/* Undoes the results of initialization. + * + * Normally this function only needs to be called from netdev_close(). + * However, it may be called by providers due to an error on opening + * that occurs after initialization. It this case netdev_close() would + * never be called. */ +void +netdev_uninit(struct netdev *netdev, bool close) +{ + /* Restore flags that we changed, if any. */ + int error = restore_flags(netdev); + list_remove(&netdev->node); + if (error) { + VLOG_WARN("failed to restore network device flags on %s: %s", + netdev_get_name(netdev), strerror(error)); + } + + if (close) { + netdev_get_dev(netdev)->class->close(netdev); + } +} + + /* Returns the class type of 'netdev'. * * The caller must not free the returned value. */ -const char *netdev_get_type(const struct netdev *netdev) +const char * +netdev_get_type(const struct netdev *netdev) +{ + return netdev_get_dev(netdev)->class->type; +} + +struct netdev_dev * +netdev_get_dev(const struct netdev *netdev) { - return netdev->class->type; + return netdev->netdev_dev; } /* Initializes 'notifier' as a netdev notifier for 'netdev', for which @@ -921,7 +976,7 @@ netdev_monitor_destroy(struct netdev_monitor *monitor) SHASH_FOR_EACH (node, &monitor->polled_netdevs) { struct netdev_notifier *notifier = node->data; - notifier->netdev->class->poll_remove(notifier); + netdev_get_dev(notifier->netdev)->class->poll_remove(notifier); } shash_destroy(&monitor->polled_netdevs); @@ -951,11 +1006,12 @@ netdev_monitor_add(struct netdev_monitor *monitor, struct netdev *netdev) const char *netdev_name = netdev_get_name(netdev); int error = 0; if (!shash_find(&monitor->polled_netdevs, netdev_name) - && netdev->class->poll_add) + && netdev_get_dev(netdev)->class->poll_add) { struct netdev_notifier *notifier; - error = netdev->class->poll_add(netdev, netdev_monitor_cb, monitor, - ¬ifier); + error = netdev_get_dev(netdev)->class->poll_add(netdev, + netdev_monitor_cb, + monitor, ¬ifier); if (!error) { assert(notifier->netdev == netdev); shash_add(&monitor->polled_netdevs, netdev_name, notifier); @@ -977,7 +1033,7 @@ netdev_monitor_remove(struct netdev_monitor *monitor, struct netdev *netdev) if (node) { /* Cancel future notifications. */ struct netdev_notifier *notifier = node->data; - netdev->class->poll_remove(notifier); + netdev_get_dev(netdev)->class->poll_remove(notifier); shash_delete(&monitor->polled_netdevs, node); /* Drop any pending notification. */ @@ -1035,7 +1091,7 @@ restore_flags(struct netdev *netdev) if (netdev->changed_flags) { enum netdev_flags restore = netdev->save_flags & netdev->changed_flags; enum netdev_flags old_flags; - return netdev->class->update_flags(netdev, + return netdev_get_dev(netdev)->class->update_flags(netdev, netdev->changed_flags & ~restore, restore, &old_flags); } diff --git a/lib/netdev.h b/lib/netdev.h index b8c7dfb4..3f6d5eab 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,18 +76,24 @@ struct netdev_stats { uint64_t tx_window_errors; }; +struct netdev_options { + const char *name; + const char *type; + const struct shash *args; + int ethertype; + bool may_create; + bool may_open; +}; + struct netdev; int netdev_initialize(void); void netdev_run(void); void netdev_wait(void); -int netdev_create(const char *name, const char *type, - const struct shash *args); -int netdev_destroy(const char *name); -int netdev_reconfigure(const char *name, const struct shash *args); - -int netdev_open(const char *name, int ethertype, struct netdev **); +int netdev_open(struct netdev_options *, struct netdev **); +int netdev_open_default(const char *name, struct netdev **); +int netdev_reconfigure(struct netdev *, const struct shash *args); void netdev_close(struct netdev *); bool netdev_exists(const char *name); @@ -95,6 +101,7 @@ bool netdev_exists(const char *name); int netdev_enumerate(struct svec *); const char *netdev_get_name(const struct netdev *); +const char *netdev_get_type(const struct netdev *); int netdev_get_mtu(const struct netdev *, int *mtup); int netdev_get_ifindex(const struct netdev *); diff --git a/ofproto/in-band.c b/ofproto/in-band.c index 30c24e6c..857618fd 100644 --- a/ofproto/in-band.c +++ b/ofproto/in-band.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -272,8 +272,8 @@ get_remote_mac(struct in_band *ib) || strcmp(netdev_get_name(ib->remote_netdev), next_hop_dev)) { netdev_close(ib->remote_netdev); - retval = netdev_open(next_hop_dev, NETDEV_ETH_TYPE_NONE, - &ib->remote_netdev); + + retval = netdev_open_default(next_hop_dev, &ib->remote_netdev); if (retval) { VLOG_WARN_RL(&rl, "cannot open netdev %s (next hop " "to controller "IP_FMT"): %s", @@ -617,7 +617,7 @@ in_band_create(struct ofproto *ofproto, struct dpif *dpif, return error; } - error = netdev_open(local_name, NETDEV_ETH_TYPE_NONE, &local_netdev); + error = netdev_open_default(local_name, &local_netdev); if (error) { VLOG_ERR("failed to initialize in-band control: cannot open " "datapath local port %s (%s)", local_name, strerror(error)); diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 3ab9cc99..46b577af 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -345,13 +345,6 @@ ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux, p->ss_cat = switch_status_register(p->switch_status, "remote", rconn_status_cb, p->controller->rconn); - /* Almost done... */ - error = init_ports(p); - if (error) { - ofproto_destroy(p); - return error; - } - /* Pick final datapath ID. */ p->datapath_id = pick_datapath_id(p); VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id); @@ -743,6 +736,10 @@ ofproto_run1(struct ofproto *p) int error; int i; + if (shash_is_empty(&p->port_by_name)) { + init_ports(p); + } + for (i = 0; i < 50; i++) { struct ofpbuf *buf; int error; @@ -1069,13 +1066,19 @@ refresh_port_groups(struct ofproto *p) static struct ofport * make_ofport(const struct odp_port *odp_port) { + struct netdev_options netdev_options; enum netdev_flags flags; struct ofport *ofport; struct netdev *netdev; bool carrier; int error; - error = netdev_open(odp_port->devname, NETDEV_ETH_TYPE_NONE, &netdev); + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = odp_port->devname; + netdev_options.ethertype = NETDEV_ETH_TYPE_NONE; + netdev_options.may_open = true; + + error = netdev_open(&netdev_options, &netdev); if (error) { VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s " "cannot be opened (%s)", diff --git a/utilities/ovs-discover.c b/utilities/ovs-discover.c index 3aa28fad..9210d4d4 100644 --- a/utilities/ovs-discover.c +++ b/utilities/ovs-discover.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -213,7 +213,7 @@ iface_init(struct iface *iface, const char *netdev_name) * persists past program termination. */ struct netdev *netdev; - retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev); + retval = netdev_open_default(iface->name, &netdev); if (retval) { ovs_error(retval, "Could not open %s device", iface->name); return false; diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c index 5e50d72a..ee545a61 100644 --- a/utilities/ovs-dpctl.c +++ b/utilities/ovs-dpctl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -166,7 +166,7 @@ static int if_up(const char *netdev_name) struct netdev *netdev; int retval; - retval = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev); + retval = netdev_open_default(netdev_name, &netdev); if (!retval) { retval = netdev_turn_flags_on(netdev, NETDEV_UP, true); netdev_close(netdev); diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 0a8a9e09..aa48602f 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -388,19 +388,14 @@ bridge_configure_ssl(const struct ovsrec_ssl *ssl) /* Attempt to create the network device 'iface_name' through the netdev * library. */ static int -set_up_iface(const struct ovsrec_interface *iface_cfg, bool create) +set_up_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface, + bool create) { struct shash_node *node; struct shash options; - int error; + int error = 0; size_t i; - /* If a type is not explicitly declared, then assume it's an existing - * "system" device. */ - if (iface_cfg->type[0] == '\0' || !strcmp(iface_cfg->type, "system")) { - return 0; - } - shash_init(&options); for (i = 0; i < iface_cfg->n_options; i++) { shash_add(&options, iface_cfg->key_options[i], @@ -408,10 +403,35 @@ set_up_iface(const struct ovsrec_interface *iface_cfg, bool create) } if (create) { - error = netdev_create(iface_cfg->name, iface_cfg->type, &options); - } else { - /* xxx Check to make sure that the type hasn't changed. */ - error = netdev_reconfigure(iface_cfg->name, &options); + struct netdev_options netdev_options; + + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = iface_cfg->name; + netdev_options.type = iface_cfg->type; + netdev_options.args = &options; + netdev_options.ethertype = NETDEV_ETH_TYPE_NONE; + netdev_options.may_create = true; + if (iface_is_internal(iface->port->bridge, iface_cfg->name)) { + netdev_options.may_open = true; + } + + error = netdev_open(&netdev_options, &iface->netdev); + + if (iface->netdev) { + netdev_get_carrier(iface->netdev, &iface->enabled); + } + } else if (iface->netdev) { + const char *netdev_type = netdev_get_type(iface->netdev); + const char *iface_type = iface_cfg->type && strlen(iface_cfg->type) + ? iface_cfg->type : NULL; + + if (!iface_type || !strcmp(netdev_type, iface_type)) { + error = netdev_reconfigure(iface->netdev, &options); + } else { + VLOG_WARN("%s: attempting change device type from %s to %s", + iface_cfg->name, netdev_type, iface_type); + error = EINVAL; + } } SHASH_FOR_EACH (node, &options) { @@ -423,30 +443,25 @@ set_up_iface(const struct ovsrec_interface *iface_cfg, bool create) } static int -reconfigure_iface(const struct ovsrec_interface *iface_cfg) +reconfigure_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface) { - return set_up_iface(iface_cfg, false); + return set_up_iface(iface_cfg, iface, false); } - -/* iterate_and_prune_ifaces() callback function that opens the network device - * for 'iface', if it is not already open, and retrieves the interface's MAC - * address and carrier status. */ static bool -init_iface_netdev(struct bridge *br UNUSED, struct iface *iface, - void *aux UNUSED) +check_iface_netdev(struct bridge *br UNUSED, struct iface *iface, + void *aux UNUSED) { - if (iface->netdev) { - return true; - } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, - &iface->netdev)) { - netdev_get_carrier(iface->netdev, &iface->enabled); - return true; - } else { - /* If the network device can't be opened, then we're not going to try - * to do anything with this interface. */ - return false; + if (!iface->netdev) { + int error = set_up_iface(iface->cfg, iface, true); + if (error) { + VLOG_WARN("could not open netdev on %s, dropping: %s", iface->name, + strerror(error)); + return false; + } } + + return true; } static bool @@ -626,7 +641,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) if (shash_find(&cur_ifaces, if_name)) { /* Already exists, just reconfigure it. */ if (iface) { - reconfigure_iface(iface->cfg); + reconfigure_iface(iface->cfg, iface); } } else { /* Need to add to datapath. */ @@ -658,8 +673,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) char *dpid_string; bridge_fetch_dp_ifaces(br); - iterate_and_prune_ifaces(br, init_iface_netdev, NULL); + iterate_and_prune_ifaces(br, check_iface_netdev, NULL); iterate_and_prune_ifaces(br, check_iface_dp_ifidx, NULL); /* Pick local port hardware address, datapath ID. */ @@ -1062,7 +1077,6 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn, } /* Bridge reconfiguration functions. */ - static struct bridge * bridge_create(const char *name) { @@ -3024,8 +3038,9 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg) iface = shash_find_data(&old_ifaces, if_cfg->name); if (!iface) { iface = iface_create(port, if_cfg); + } else { + iface->cfg = if_cfg; } - iface->cfg = if_cfg; } /* Get VLAN tag. */ @@ -3252,7 +3267,7 @@ port_update_bond_compat(struct port *port) if (port->bond_fake_iface) { struct netdev *bond_netdev; - if (!netdev_open(port->name, NETDEV_ETH_TYPE_NONE, &bond_netdev)) { + if (!netdev_open_default(port->name, &bond_netdev)) { if (bond.up) { netdev_turn_flags_on(bond_netdev, NETDEV_UP, true); } else { @@ -3320,6 +3335,7 @@ iface_create(struct port *port, const struct ovsrec_interface *if_cfg) iface->tag = tag_create_random(); iface->delay_expires = LLONG_MAX; iface->netdev = NULL; + iface->cfg = if_cfg; if (port->n_ifaces >= port->allocated_ifaces) { port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces, @@ -3332,10 +3348,12 @@ iface_create(struct port *port, const struct ovsrec_interface *if_cfg) /* Attempt to create the network interface in case it * doesn't exist yet. */ - error = set_up_iface(if_cfg, true); - if (error) { - VLOG_WARN("could not create iface %s: %s\n", iface->name, - strerror(error)); + if (!iface_is_internal(port->bridge, iface->name)) { + error = set_up_iface(if_cfg, iface, true); + if (error) { + VLOG_WARN("could not create iface %s: %s", iface->name, + strerror(error)); + } } VLOG_DBG("attached network device %s to port %s", iface->name, port->name); @@ -3369,7 +3387,6 @@ iface_destroy(struct iface *iface) bond_send_learning_packets(port); } - netdev_destroy(iface->name); free(iface->name); free(iface); diff --git a/vswitchd/ovs-brcompatd.c b/vswitchd/ovs-brcompatd.c index a45bee11..70921f21 100644 --- a/vswitchd/ovs-brcompatd.c +++ b/vswitchd/ovs-brcompatd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2009 Nicira Networks +/* Copyright (c) 2008, 2009, 2010 Nicira Networks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -755,7 +755,7 @@ handle_fdb_query_cmd(const struct ovsrec_open_vswitch *ovs, struct mac *mac = &local_macs[n_local_macs]; struct netdev *netdev; - error = netdev_open(iface_name, NETDEV_ETH_TYPE_NONE, &netdev); + error = netdev_open_default(iface_name, &netdev); if (netdev) { if (!netdev_get_etheraddr(netdev, mac->addr)) { n_local_macs++;