X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev.c;h=44675185e3bbebf320d1c9abc0a6897d0eae8eb1;hb=060ea9d5152b5c8cc6646ead1175484e07c583fe;hp=2d6360b58c6a697a5ef8892448e65e814bc8034d;hpb=7fa0f73fb284b4406bcd085ee62552891b3fa6cd;p=sliver-openvswitch.git diff --git a/lib/netdev.c b/lib/netdev.c index 2d6360b58..44675185e 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -48,6 +48,13 @@ COVERAGE_DEFINE(netdev_sent); COVERAGE_DEFINE(netdev_add_router); COVERAGE_DEFINE(netdev_get_stats); +struct netdev_saved_flags { + struct netdev_dev *dev; + struct list node; /* In struct netdev_dev's saved_flags_list. */ + enum netdev_flags saved_flags; + enum netdev_flags saved_values; +}; + static struct shash netdev_classes = SHASH_INITIALIZER(&netdev_classes); /* All created network devices. */ @@ -60,8 +67,7 @@ static struct list netdev_list = LIST_INITIALIZER(&netdev_list); * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); -static void close_all_netdevs(void *aux OVS_UNUSED); -static int restore_flags(struct netdev *netdev); +static void restore_all_flags(void *aux OVS_UNUSED); void update_device_args(struct netdev_dev *, const struct shash *args); static void @@ -72,7 +78,7 @@ netdev_initialize(void) if (!inited) { inited = true; - fatal_signal_add_hook(close_all_netdevs, NULL, NULL, true); + fatal_signal_add_hook(restore_all_flags, NULL, NULL, true); netdev_vport_patch_register(); #ifdef LINUX_DATAPATH @@ -304,21 +310,24 @@ netdev_get_tunnel_config(const struct netdev *netdev) } } +static void +netdev_dev_unref(struct netdev_dev *dev) +{ + ovs_assert(dev->ref_cnt); + if (!--dev->ref_cnt) { + netdev_dev_uninit(dev, true); + } +} + /* Closes and destroys 'netdev'. */ void netdev_close(struct netdev *netdev) { if (netdev) { - struct netdev_dev *netdev_dev = netdev_get_dev(netdev); + struct netdev_dev *dev = netdev_get_dev(netdev); - ovs_assert(netdev_dev->ref_cnt); - netdev_dev->ref_cnt--; netdev_uninit(netdev, true); - - /* If the reference count for the netdev device is zero, destroy it. */ - if (!netdev_dev->ref_cnt) { - netdev_dev_uninit(netdev_dev, true); - } + netdev_dev_unref(dev); } } @@ -341,49 +350,44 @@ netdev_parse_name(const char *netdev_name_, char **name, char **type) } } -/* Attempts to set up 'netdev' for receiving packets with netdev_recv(). - * Returns 0 if successful, otherwise a positive errno value. EOPNOTSUPP - * indicates that the network device does not implement packet reception - * through this interface. */ int -netdev_listen(struct netdev *netdev) +netdev_rx_open(struct netdev *netdev, struct netdev_rx **rxp) { - int (*listen)(struct netdev *); + struct netdev_dev *dev = netdev_get_dev(netdev); + int error; - listen = netdev_get_dev(netdev)->netdev_class->listen; - return listen ? (listen)(netdev) : EOPNOTSUPP; + error = (dev->netdev_class->rx_open + ? dev->netdev_class->rx_open(netdev, rxp) + : EOPNOTSUPP); + if (!error) { + ovs_assert((*rxp)->netdev_dev == dev); + dev->ref_cnt++; + } else { + *rxp = NULL; + } + return error; +} + +void +netdev_rx_close(struct netdev_rx *rx) +{ + if (rx) { + struct netdev_dev *dev = rx->netdev_dev; + + rx->rx_class->destroy(rx); + netdev_dev_unref(dev); + } } -/* Attempts to receive a packet from 'netdev' into 'buffer', which the caller - * must have initialized with sufficient room for the packet. The space - * required to receive any packet is ETH_HEADER_LEN bytes, plus VLAN_HEADER_LEN - * bytes, plus the device's MTU (which may be retrieved via netdev_get_mtu()). - * (Some devices do not allow for a VLAN header, in which case VLAN_HEADER_LEN - * need not be included.) - * - * This function can only be expected to return a packet if ->listen() has - * been called successfully. - * - * If a packet is successfully retrieved, returns 0. In this case 'buffer' is - * guaranteed to contain at least ETH_TOTAL_MIN bytes. Otherwise, returns a - * positive errno value. Returns EAGAIN immediately if no packet is ready to - * be returned. - * - * Some network devices may not implement support for this function. In such - * cases this function will always return EOPNOTSUPP. */ int -netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) +netdev_rx_recv(struct netdev_rx *rx, struct ofpbuf *buffer) { - int (*recv)(struct netdev *, void *, size_t); int retval; ovs_assert(buffer->size == 0); ovs_assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN); - recv = netdev_get_dev(netdev)->netdev_class->recv; - retval = (recv - ? (recv)(netdev, buffer->data, ofpbuf_tailroom(buffer)) - : -EOPNOTSUPP); + retval = rx->rx_class->recv(rx, buffer->data, ofpbuf_tailroom(buffer)); if (retval >= 0) { COVERAGE_INC(netdev_received); buffer->size += retval; @@ -396,27 +400,16 @@ netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) } } -/* Registers with the poll loop to wake up from the next call to poll_block() - * when a packet is ready to be received with netdev_recv() on 'netdev'. */ void -netdev_recv_wait(struct netdev *netdev) +netdev_rx_wait(struct netdev_rx *rx) { - void (*recv_wait)(struct netdev *); - - recv_wait = netdev_get_dev(netdev)->netdev_class->recv_wait; - if (recv_wait) { - recv_wait(netdev); - } + rx->rx_class->wait(rx); } -/* Discards all packets waiting to be received from 'netdev'. */ int -netdev_drain(struct netdev *netdev) +netdev_rx_drain(struct netdev_rx *rx) { - int (*drain)(struct netdev *); - - drain = netdev_get_dev(netdev)->netdev_class->drain; - return drain ? drain(netdev) : 0; + return rx->rx_class->drain ? rx->rx_class->drain(rx) : 0; } /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive @@ -793,37 +786,44 @@ netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6) } /* On 'netdev', turns off the flags in 'off' and then turns on the flags in - * 'on'. If 'permanent' is true, the changes will persist; otherwise, they - * will be reverted when 'netdev' is closed or the program exits. Returns 0 if - * successful, otherwise a positive errno value. */ + * 'on'. Returns 0 if successful, otherwise a positive errno value. */ static int do_update_flags(struct netdev *netdev, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp, - bool permanent) + struct netdev_saved_flags **sfp) { + struct netdev_dev *dev = netdev_get_dev(netdev); + struct netdev_saved_flags *sf = NULL; enum netdev_flags old_flags; int error; - error = netdev_get_dev(netdev)->netdev_class->update_flags(netdev, - off & ~on, on, &old_flags); + error = dev->netdev_class->update_flags(dev, 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), strerror(error)); old_flags = 0; - } else if ((off || on) && !permanent) { + } else if ((off || on) && sfp) { enum netdev_flags new_flags = (old_flags & ~off) | on; enum netdev_flags changed_flags = old_flags ^ new_flags; if (changed_flags) { - if (!netdev->changed_flags) { - netdev->save_flags = old_flags; - } - netdev->changed_flags |= changed_flags; + *sfp = sf = xmalloc(sizeof *sf); + sf->dev = dev; + list_push_front(&dev->saved_flags_list, &sf->node); + sf->saved_flags = changed_flags; + sf->saved_values = changed_flags & new_flags; + + dev->ref_cnt++; } } + if (old_flagsp) { *old_flagsp = old_flags; } + if (sfp) { + *sfp = sf; + } + return error; } @@ -834,40 +834,63 @@ int netdev_get_flags(const struct netdev *netdev_, enum netdev_flags *flagsp) { struct netdev *netdev = CONST_CAST(struct netdev *, netdev_); - return do_update_flags(netdev, 0, 0, flagsp, false); + return do_update_flags(netdev, 0, 0, flagsp, NULL); } /* Sets the flags for 'netdev' to 'flags'. - * If 'permanent' is true, the changes will persist; otherwise, they - * will be reverted when 'netdev' is closed or the program exits. * Returns 0 if successful, otherwise a positive errno value. */ int netdev_set_flags(struct netdev *netdev, enum netdev_flags flags, - bool permanent) + struct netdev_saved_flags **sfp) { - return do_update_flags(netdev, -1, flags, NULL, permanent); + return do_update_flags(netdev, -1, flags, NULL, sfp); } -/* Turns on the specified 'flags' on 'netdev'. - * If 'permanent' is true, the changes will persist; otherwise, they - * will be reverted when 'netdev' is closed or the program exits. - * Returns 0 if successful, otherwise a positive errno value. */ +/* Turns on the specified 'flags' on 'netdev': + * + * - On success, returns 0. If 'sfp' is nonnull, sets '*sfp' to a newly + * allocated 'struct netdev_saved_flags *' that may be passed to + * netdev_restore_flags() to restore the original values of 'flags' on + * 'netdev' (this will happen automatically at program termination if + * netdev_restore_flags() is never called) , or to NULL if no flags were + * actually changed. + * + * - On failure, returns a positive errno value. If 'sfp' is nonnull, sets + * '*sfp' to NULL. */ int netdev_turn_flags_on(struct netdev *netdev, enum netdev_flags flags, - bool permanent) + struct netdev_saved_flags **sfp) { - return do_update_flags(netdev, 0, flags, NULL, permanent); + return do_update_flags(netdev, 0, flags, NULL, sfp); } -/* Turns off the specified 'flags' on 'netdev'. - * If 'permanent' is true, the changes will persist; otherwise, they - * will be reverted when 'netdev' is closed or the program exits. - * Returns 0 if successful, otherwise a positive errno value. */ +/* Turns off the specified 'flags' on 'netdev'. See netdev_turn_flags_on() for + * details of the interface. */ int netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags, - bool permanent) + struct netdev_saved_flags **sfp) +{ + return do_update_flags(netdev, flags, 0, NULL, sfp); +} + +/* Restores the flags that were saved in 'sf', and destroys 'sf'. + * Does nothing if 'sf' is NULL. */ +void +netdev_restore_flags(struct netdev_saved_flags *sf) { - return do_update_flags(netdev, flags, 0, NULL, permanent); + if (sf) { + struct netdev_dev *dev = sf->dev; + enum netdev_flags old_flags; + + dev->netdev_class->update_flags(dev, + sf->saved_flags & sf->saved_values, + sf->saved_flags & ~sf->saved_values, + &old_flags); + list_remove(&sf->node); + free(sf); + + netdev_dev_unref(dev); + } } /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be @@ -1117,7 +1140,7 @@ netdev_set_qos(struct netdev *netdev, if (class->set_qos) { if (!details) { - static struct smap empty = SMAP_INITIALIZER(&empty); + static const struct smap empty = SMAP_INITIALIZER(&empty); details = ∅ } return class->set_qos(netdev, type, details); @@ -1295,6 +1318,7 @@ netdev_dev_init(struct netdev_dev *netdev_dev, const char *name, netdev_dev->netdev_class = netdev_class; netdev_dev->name = xstrdup(name); netdev_dev->node = shash_add(&netdev_dev_shash, name, netdev_dev); + list_init(&netdev_dev->saved_flags_list); } /* Undoes the results of initialization. @@ -1310,6 +1334,7 @@ netdev_dev_uninit(struct netdev_dev *netdev_dev, bool destroy) char *name = netdev_dev->name; ovs_assert(!netdev_dev->ref_cnt); + ovs_assert(list_is_empty(&netdev_dev->saved_flags_list)); shash_delete(&netdev_dev_shash, netdev_dev->node); @@ -1392,20 +1417,12 @@ netdev_init(struct netdev *netdev, struct netdev_dev *netdev_dev) 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)->netdev_class->close(netdev); } } - /* Returns the class type of 'netdev'. * * The caller must not free the returned value. */ @@ -1415,7 +1432,6 @@ netdev_get_type(const struct netdev *netdev) return netdev_get_dev(netdev)->netdev_class->type; } - const char * netdev_get_type_from_name(const char *name) { @@ -1429,31 +1445,58 @@ netdev_get_dev(const struct netdev *netdev) return netdev->netdev_dev; } -/* Restore the network device flags on 'netdev' to those that were active - * before we changed them. Returns 0 if successful, otherwise a positive - * errno value. - * - * To avoid reentry, the caller must ensure that fatal signals are blocked. */ -static int -restore_flags(struct netdev *netdev) +void +netdev_rx_init(struct netdev_rx *rx, struct netdev_dev *dev, + const struct netdev_rx_class *class) { - if (netdev->changed_flags) { - enum netdev_flags restore = netdev->save_flags & netdev->changed_flags; - enum netdev_flags old_flags; - return netdev_get_dev(netdev)->netdev_class->update_flags(netdev, - netdev->changed_flags & ~restore, - restore, &old_flags); - } - return 0; + ovs_assert(dev->ref_cnt > 0); + rx->rx_class = class; + rx->netdev_dev = dev; +} + +void +netdev_rx_uninit(struct netdev_rx *rx OVS_UNUSED) +{ + /* Nothing to do. */ +} + +struct netdev_dev * +netdev_rx_get_dev(const struct netdev_rx *rx) +{ + ovs_assert(rx->netdev_dev->ref_cnt > 0); + return rx->netdev_dev; +} + +const char * +netdev_rx_get_name(const struct netdev_rx *rx) +{ + return netdev_dev_get_name(netdev_rx_get_dev(rx)); } -/* Close all netdevs on shutdown so they can do any needed cleanup such as - * destroying devices, restoring flags, etc. */ static void -close_all_netdevs(void *aux OVS_UNUSED) +restore_all_flags(void *aux OVS_UNUSED) { - struct netdev *netdev, *next; - LIST_FOR_EACH_SAFE(netdev, next, node, &netdev_list) { - netdev_close(netdev); + struct shash_node *node; + + SHASH_FOR_EACH (node, &netdev_dev_shash) { + struct netdev_dev *dev = node->data; + const struct netdev_saved_flags *sf; + enum netdev_flags saved_values; + enum netdev_flags saved_flags; + + saved_values = saved_flags = 0; + LIST_FOR_EACH (sf, node, &dev->saved_flags_list) { + saved_flags |= sf->saved_flags; + saved_values &= ~sf->saved_flags; + saved_values |= sf->saved_flags & sf->saved_values; + } + if (saved_flags) { + enum netdev_flags old_flags; + + dev->netdev_class->update_flags(dev, + saved_flags & saved_values, + saved_flags & ~saved_values, + &old_flags); + } } }