Merge "sflow" into "master".
authorBen Pfaff <blp@nicira.com>
Mon, 25 Jan 2010 18:52:28 +0000 (10:52 -0800)
committerBen Pfaff <blp@nicira.com>
Mon, 25 Jan 2010 18:52:28 +0000 (10:52 -0800)
No conflicts, but lib/dpif.c needed a few changes since struct dpif's
member "class" was renamed to "dpif_class" in master since sflow was
branched off.

1  2 
configure.ac
datapath/datapath.c
datapath/datapath.h
lib/automake.mk
lib/dpif-provider.h
lib/dpif.c
lib/netdev.c
vswitchd/ovs-vswitchd.conf.5.in

diff --combined configure.ac
@@@ -13,7 -13,7 +13,7 @@@
  # limitations under the License.
  
  AC_PREREQ(2.63)
 -AC_INIT(openvswitch, 0.90.6, ovs-bugs@openvswitch.org)
 +AC_INIT(openvswitch, 0.99.0, ovs-bugs@openvswitch.org)
  NX_BUILDNR
  AC_CONFIG_SRCDIR([datapath/datapath.c])
  AC_CONFIG_MACRO_DIR([m4])
@@@ -75,6 -75,7 +75,7 @@@ OVS_ENABLE_OPTION([-Wold-style-definiti
  OVS_ENABLE_OPTION([-Wmissing-prototypes])
  OVS_ENABLE_OPTION([-Wmissing-field-initializers])
  OVS_ENABLE_OPTION([-Wno-override-init])
+ OVS_CONDITIONAL_CC_OPTION([-Wno-unused], [HAVE_WNO_UNUSED])
  
  AC_ARG_VAR(KARCH, [Kernel Architecture String])
  AC_SUBST(KARCH)
diff --combined datapath/datapath.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2007, 2008, 2009 Nicira Networks.
+  * Copyright (c) 2007, 2008, 2009, 2010 Nicira Networks.
   * Distributed under the terms of the GNU GPL version 2.
   *
   * Significant portions of this file may be copied from parts of the Linux
@@@ -349,6 -349,7 +349,7 @@@ static int new_nbp(struct datapath *dp
        p->port_no = port_no;
        p->dp = dp;
        p->dev = dev;
+       atomic_set(&p->sflow_pool, 0);
        if (!is_dp_dev(dev))
                rcu_assign_pointer(dev->br_port, p);
        else {
@@@ -621,7 -622,9 +622,7 @@@ int vswitch_skb_checksum_setup(struct s
  out:
        return -EPROTO;
  }
 -#else
 -int vswitch_skb_checksum_setup(struct sk_buff *skb) { return 0; }
 -#endif /* CONFIG_XEN && linux == 2.6.18 */
 +#endif /* CONFIG_XEN && HAVE_PROTO_DATA_VALID */
  
  /* Append each packet in 'skb' list to 'queue'.  There will be only one packet
   * unless we broke up a GSO packet. */
@@@ -713,8 -716,7 +714,7 @@@ dp_output_control(struct datapath *dp, 
        int err;
  
        WARN_ON_ONCE(skb_shared(skb));
-       BUG_ON(queue_no != _ODPL_MISS_NR && queue_no != _ODPL_ACTION_NR);
+       BUG_ON(queue_no != _ODPL_MISS_NR && queue_no != _ODPL_ACTION_NR && queue_no != _ODPL_SFLOW_NR);
        queue = &dp->queues[queue_no];
        err = -ENOBUFS;
        if (skb_queue_len(queue) >= DP_MAX_QUEUE_LEN)
@@@ -1391,6 -1393,7 +1391,7 @@@ static long openvswitch_ioctl(struct fi
        int dp_idx = iminor(f->f_dentry->d_inode);
        struct datapath *dp;
        int drop_frags, listeners, port_no;
+       unsigned int sflow_probability;
        int err;
  
        /* Handle commands with special locking requirements up front. */
                set_listen_mask(f, listeners);
                break;
  
+       case ODP_GET_SFLOW_PROBABILITY:
+               err = put_user(dp->sflow_probability, (unsigned int __user *)argp);
+               break;
+       case ODP_SET_SFLOW_PROBABILITY:
+               err = get_user(sflow_probability, (unsigned int __user *)argp);
+               if (!err)
+                       dp->sflow_probability = sflow_probability;
+               break;
        case ODP_PORT_QUERY:
                err = query_port(dp, (struct odp_port __user *)argp);
                break;
diff --combined datapath/datapath.h
@@@ -1,5 -1,5 +1,5 @@@
  /*
-  * Copyright (c) 2009 Nicira Networks.
+  * Copyright (c) 2009, 2010 Nicira Networks.
   * Distributed under the terms of the GNU GPL version 2.
   *
   * Significant portions of this file may be copied from parts of the Linux
@@@ -79,9 -79,22 +79,22 @@@ struct dp_bucket 
        struct sw_flow *flows[];
  };
  
- #define DP_N_QUEUES 2
+ #define DP_N_QUEUES 3
  #define DP_MAX_QUEUE_LEN 100
  
+ /**
+  * struct dp_stats_percpu - per-cpu packet processing statistics for a given
+  * datapath.
+  * @n_frags: Number of IP fragments processed by datapath.
+  * @n_hit: Number of received packets for which a matching flow was found in
+  * the flow table.
+  * @n_miss: Number of received packets that had no matching flow in the flow
+  * table.  The sum of @n_hit and @n_miss is the number of packets that have
+  * been received by the datapath.
+  * @n_lost: Number of received packets that had no matching flow in the flow
+  * table that could not be sent to userspace (normally due to an overflow in
+  * one of the datapath's queues).
+  */
  struct dp_stats_percpu {
        u64 n_frags;
        u64 n_hit;
@@@ -95,10 -108,29 +108,29 @@@ struct dp_port_group 
        u16 ports[];
  };
  
+ /**
+  * struct datapath - datapath for flow-based packet switching
+  * @mutex: Mutual exclusion for ioctls.
+  * @dp_idx: Datapath number (index into the dps[] array in datapath.c).
+  * @ifobj: Represents /sys/class/net/<devname>/brif.
+  * @drop_frags: Drop all IP fragments if nonzero.
+  * @queues: %DP_N_QUEUES sets of queued packets for userspace to handle.
+  * @waitqueue: Waitqueue, for waiting for new packets in @queues.
+  * @n_flows: Number of flows currently in flow table.
+  * @table: Current flow table (RCU protected).
+  * @groups: Port groups, used by ODPAT_OUTPUT_GROUP action (RCU protected).
+  * @n_ports: Number of ports currently in @ports.
+  * @ports: Map from port number to &struct net_bridge_port.  %ODPP_LOCAL port
+  * always exists, other ports may be %NULL.
+  * @port_list: List of all ports in @ports in arbitrary order.
+  * @stats_percpu: Per-CPU datapath statistics.
+  * @sflow_probability: Number of packets out of UINT_MAX to sample to the
+  * %ODPL_SFLOW queue, e.g. (@sflow_probability/UINT_MAX) is the probability of
+  * sampling a given packet.
+  */
  struct datapath {
        struct mutex mutex;
        int dp_idx;
        struct kobject ifobj;
  
        int drop_frags;
        /* Switch ports. */
        unsigned int n_ports;
        struct net_bridge_port *ports[DP_MAX_PORTS];
-       struct list_head port_list; /* All ports, including local_port. */
+       struct list_head port_list;
  
        /* Stats. */
        struct dp_stats_percpu *stats_percpu;
+       /* sFlow Sampling */
+       unsigned int sflow_probability;
  };
  
+ /**
+  * struct net_bridge_port - one port within a datapath
+  * @port_no: Index into @dp's @ports array.
+  * @dp: Datapath to which this port belongs.
+  * @dev: The network device attached to this port.  The @br_port member in @dev
+  * points back to this &struct net_bridge_port.
+  * @kobj: Represents /sys/class/net/<devname>/brport.
+  * @linkname: The name of the link from /sys/class/net/<datapath>/brif to this
+  * &struct net_bridge_port.  (We keep this around so that we can delete it
+  * if @dev gets renamed.)  Set to the null string when no link exists.
+  * @node: Element in @dp's @port_list.
+  * @sflow_pool: Number of packets that were candidates for sFlow sampling,
+  * regardless of whether they were actually chosen and sent down to userspace.
+  */
  struct net_bridge_port {
        u16 port_no;
        struct datapath *dp;
        struct net_device *dev;
        struct kobject kobj;
        char linkname[IFNAMSIZ];
-       struct list_head node;   /* Element in datapath.ports. */
+       struct list_head node;
+       atomic_t sflow_pool;
  };
  
  extern struct notifier_block dp_device_notifier;
@@@ -159,13 -209,15 +209,13 @@@ static inline const char *dp_name(cons
        return dp->ports[ODPP_LOCAL]->dev->name;
  }
  
 -#ifdef CONFIG_XEN
 -int skb_checksum_setup(struct sk_buff *skb);
 +#if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
 +int vswitch_skb_checksum_setup(struct sk_buff *skb);
  #else
 -static inline int skb_checksum_setup(struct sk_buff *skb)
 +static inline int vswitch_skb_checksum_setup(struct sk_buff *skb)
  {
        return 0;
  }
  #endif
  
 -int vswitch_skb_checksum_setup(struct sk_buff *skb);
 -
  #endif /* datapath.h */
diff --combined lib/automake.mk
@@@ -1,4 -1,4 +1,4 @@@
 -# Copyright (C) 2009 Nicira Networks, Inc.
 +# Copyright (C) 2009, 2010 Nicira Networks, Inc.
  #
  # Copying and distribution of this file, with or without modification,
  # are permitted in any medium without royalty provided the copyright
@@@ -96,7 -96,6 +96,7 @@@ lib_libopenvswitch_a_SOURCES = 
        lib/socket-util.h \
        lib/stp.c \
        lib/stp.h \
 +      lib/string.h \
        lib/svec.c \
        lib/svec.h \
        lib/tag.c \
@@@ -126,6 -125,19 +126,19 @@@ nodist_lib_libopenvswitch_a_SOURCES = 
        lib/dirs.c
  CLEANFILES += $(nodist_lib_libopenvswitch_a_SOURCES)
  
+ noinst_LIBRARIES += lib/libsflow.a
+ lib_libsflow_a_SOURCES = \
+       lib/sflow_api.h \
+       lib/sflow.h \
+       lib/sflow_agent.c \
+       lib/sflow_sampler.c \
+       lib/sflow_poller.c \
+       lib/sflow_receiver.c
+ lib_libsflow_a_CFLAGS = $(AM_CFLAGS)
+ if HAVE_WNO_UNUSED
+ lib_libsflow_a_CFLAGS += -Wno-unused
+ endif
  if HAVE_NETLINK
  lib_libopenvswitch_a_SOURCES += \
        lib/netlink-protocol.h \
diff --combined lib/dpif-provider.h
@@@ -1,5 -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.
  #include <assert.h>
  #include "dpif.h"
  
 +#ifdef  __cplusplus
 +extern "C" {
 +#endif
 +
  /* Open vSwitch datapath interface.
   *
   * This structure should be treated as opaque by dpif implementations. */
  struct dpif {
 -    const struct dpif_class *class;
 +    const struct dpif_class *dpif_class;
      char *name;
      uint8_t netflow_engine_type;
      uint8_t netflow_engine_id;
@@@ -40,9 -36,9 +40,9 @@@
  void dpif_init(struct dpif *, const struct dpif_class *, const char *name,
                 uint8_t netflow_engine_type, uint8_t netflow_engine_id);
  static inline void dpif_assert_class(const struct dpif *dpif,
 -                                     const struct dpif_class *class)
 +                                     const struct dpif_class *dpif_class)
  {
 -    assert(dpif->class == class);
 +    assert(dpif->dpif_class == dpif_class);
  }
  
  /* Datapath interface class structure, to be defined by each implementation of
@@@ -121,7 -117,7 +121,7 @@@ struct dpif_class 
       *
       * If successful, 'dpif' will not be used again except as an argument for
       * the 'close' member function. */
 -    int (*delete)(struct dpif *dpif);
 +    int (*destroy)(struct dpif *dpif);
  
      /* Retrieves statistics for 'dpif' into 'stats'. */
      int (*get_stats)(const struct dpif *dpif, struct odp_stats *stats);
       * corresponding type when it calls the recv member function. */
      int (*recv_set_mask)(struct dpif *dpif, int listen_mask);
  
+     /* Retrieves 'dpif''s sFlow sampling probability into '*probability'.
+      * Return value is 0 or a positive errno value.  EOPNOTSUPP indicates that
+      * the datapath does not support sFlow, as does a null pointer.
+      *
+      * '*probability' is expressed as the number of packets out of UINT_MAX to
+      * sample, e.g. probability/UINT_MAX is the probability of sampling a given
+      * packet. */
+     int (*get_sflow_probability)(const struct dpif *dpif,
+                                  uint32_t *probability);
+     /* Sets 'dpif''s sFlow sampling probability to 'probability'.  Return value
+      * is 0 or a positive errno value.  EOPNOTSUPP indicates that the datapath
+      * does not support sFlow, as does a null pointer.
+      *
+      * 'probability' is expressed as the number of packets out of UINT_MAX to
+      * sample, e.g. probability/UINT_MAX is the probability of sampling a given
+      * packet. */
+     int (*set_sflow_probability)(struct dpif *dpif, uint32_t probability);
      /* Attempts to receive a message from 'dpif'.  If successful, stores the
       * message into '*packetp'.  The message, if one is received, must begin
       * with 'struct odp_msg' as a header.  Only messages of the types selected
  extern const struct dpif_class dpif_linux_class;
  extern const struct dpif_class dpif_netdev_class;
  
 +#ifdef  __cplusplus
 +}
 +#endif
 +
  #endif /* dpif-provider.h */
diff --combined lib/dpif.c
@@@ -1,5 -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.
@@@ -205,7 -205,7 +205,7 @@@ dpif_close(struct dpif *dpif
  {
      if (dpif) {
          char *name = dpif->name;
 -        dpif->class->close(dpif);
 +        dpif->dpif_class->close(dpif);
          free(name);
      }
  }
@@@ -229,8 -229,8 +229,8 @@@ dpif_name(const struct dpif *dpif
  int
  dpif_get_all_names(const struct dpif *dpif, struct svec *all_names)
  {
 -    if (dpif->class->get_all_names) {
 -        int error = dpif->class->get_all_names(dpif, all_names);
 +    if (dpif->dpif_class->get_all_names) {
 +        int error = dpif->dpif_class->get_all_names(dpif, all_names);
          if (error) {
              VLOG_WARN_RL(&error_rl,
                           "failed to retrieve names for datpath %s: %s",
@@@ -253,7 -253,7 +253,7 @@@ dpif_delete(struct dpif *dpif
  
      COVERAGE_INC(dpif_destroy);
  
 -    error = dpif->class->delete(dpif);
 +    error = dpif->dpif_class->destroy(dpif);
      log_operation(dpif, "delete", error);
      return error;
  }
  int
  dpif_get_dp_stats(const struct dpif *dpif, struct odp_stats *stats)
  {
 -    int error = dpif->class->get_stats(dpif, stats);
 +    int error = dpif->dpif_class->get_stats(dpif, stats);
      if (error) {
          memset(stats, 0, sizeof *stats);
      }
  int
  dpif_get_drop_frags(const struct dpif *dpif, bool *drop_frags)
  {
 -    int error = dpif->class->get_drop_frags(dpif, drop_frags);
 +    int error = dpif->dpif_class->get_drop_frags(dpif, drop_frags);
      if (error) {
          *drop_frags = false;
      }
  int
  dpif_set_drop_frags(struct dpif *dpif, bool drop_frags)
  {
 -    int error = dpif->class->set_drop_frags(dpif, drop_frags);
 +    int error = dpif->dpif_class->set_drop_frags(dpif, drop_frags);
      log_operation(dpif, "set_drop_frags", error);
      return error;
  }
@@@ -312,7 -312,7 +312,7 @@@ dpif_port_add(struct dpif *dpif, const 
  
      COVERAGE_INC(dpif_port_add);
  
 -    error = dpif->class->port_add(dpif, devname, flags, &port_no);
 +    error = dpif->dpif_class->port_add(dpif, devname, flags, &port_no);
      if (!error) {
          VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16,
                      dpif_name(dpif), devname, port_no);
@@@ -336,7 -336,7 +336,7 @@@ dpif_port_del(struct dpif *dpif, uint16
  
      COVERAGE_INC(dpif_port_del);
  
 -    error = dpif->class->port_del(dpif, port_no);
 +    error = dpif->dpif_class->port_del(dpif, port_no);
      log_operation(dpif, "port_del", error);
      return error;
  }
@@@ -348,7 -348,7 +348,7 @@@ in
  dpif_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
                            struct odp_port *port)
  {
 -    int error = dpif->class->port_query_by_number(dpif, port_no, port);
 +    int error = dpif->dpif_class->port_query_by_number(dpif, port_no, port);
      if (!error) {
          VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu16" is device %s",
                      dpif_name(dpif), port_no, port->devname);
@@@ -367,7 -367,7 +367,7 @@@ in
  dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
                          struct odp_port *port)
  {
 -    int error = dpif->class->port_query_by_name(dpif, devname, port);
 +    int error = dpif->dpif_class->port_query_by_name(dpif, devname, port);
      if (!error) {
          VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu16,
                      dpif_name(dpif), devname, port->port);
@@@ -432,7 -432,7 +432,7 @@@ dpif_port_list(const struct dpif *dpif
          }
  
          ports = xcalloc(stats.n_ports, sizeof *ports);
 -        retval = dpif->class->port_list(dpif, ports, stats.n_ports);
 +        retval = dpif->dpif_class->port_list(dpif, ports, stats.n_ports);
          if (retval < 0) {
              /* Hard error. */
              error = -retval;
@@@ -480,7 -480,7 +480,7 @@@ exit
  int
  dpif_port_poll(const struct dpif *dpif, char **devnamep)
  {
 -    int error = dpif->class->port_poll(dpif, devnamep);
 +    int error = dpif->dpif_class->port_poll(dpif, devnamep);
      if (error) {
          *devnamep = NULL;
      }
  void
  dpif_port_poll_wait(const struct dpif *dpif)
  {
 -    dpif->class->port_poll_wait(dpif);
 +    dpif->dpif_class->port_poll_wait(dpif);
  }
  
  /* Retrieves a list of the port numbers in port group 'group' in 'dpif'.
@@@ -513,8 -513,8 +513,8 @@@ dpif_port_group_get(const struct dpif *
      *ports = NULL;
      *n_ports = 0;
      for (;;) {
 -        int retval = dpif->class->port_group_get(dpif, group,
 -                                                 *ports, *n_ports);
 +        int retval = dpif->dpif_class->port_group_get(dpif, group,
 +                                                      *ports, *n_ports);
          if (retval < 0) {
              /* Hard error. */
              error = -retval;
@@@ -552,7 -552,7 +552,7 @@@ dpif_port_group_set(struct dpif *dpif, 
  
      COVERAGE_INC(dpif_port_group_set);
  
 -    error = dpif->class->port_group_set(dpif, group, ports, n_ports);
 +    error = dpif->dpif_class->port_group_set(dpif, group, ports, n_ports);
      log_operation(dpif, "port_group_set", error);
      return error;
  }
@@@ -566,7 -566,7 +566,7 @@@ dpif_flow_flush(struct dpif *dpif
  
      COVERAGE_INC(dpif_flow_flush);
  
 -    error = dpif->class->flow_flush(dpif);
 +    error = dpif->dpif_class->flow_flush(dpif);
      log_operation(dpif, "flow_flush", error);
      return error;
  }
@@@ -592,7 -592,7 +592,7 @@@ dpif_flow_get(const struct dpif *dpif, 
      COVERAGE_INC(dpif_flow_get);
  
      check_rw_odp_flow(flow);
 -    error = dpif->class->flow_get(dpif, flow, 1);
 +    error = dpif->dpif_class->flow_get(dpif, flow, 1);
      if (!error) {
          error = flow->stats.error;
      }
@@@ -642,7 -642,7 +642,7 @@@ dpif_flow_get_multiple(const struct dpi
          check_rw_odp_flow(&flows[i]);
      }
  
 -    error = dpif->class->flow_get(dpif, flows, n);
 +    error = dpif->dpif_class->flow_get(dpif, flows, n);
      log_operation(dpif, "flow_get_multiple", error);
      return error;
  }
@@@ -670,7 -670,7 +670,7 @@@ dpif_flow_put(struct dpif *dpif, struc
  
      COVERAGE_INC(dpif_flow_put);
  
 -    error = dpif->class->flow_put(dpif, put);
 +    error = dpif->dpif_class->flow_put(dpif, put);
      if (should_log_flow_message(error)) {
          log_flow_put(dpif, error, put);
      }
@@@ -692,7 -692,7 +692,7 @@@ dpif_flow_del(struct dpif *dpif, struc
      check_rw_odp_flow(flow);
      memset(&flow->stats, 0, sizeof flow->stats);
  
 -    error = dpif->class->flow_del(dpif, flow);
 +    error = dpif->dpif_class->flow_del(dpif, flow);
      if (should_log_flow_message(error)) {
          log_flow_operation(dpif, "delete flow", error, flow);
      }
@@@ -721,7 -721,7 +721,7 @@@ dpif_flow_list(const struct dpif *dpif
              flows[i].n_actions = 0;
          }
      }
 -    retval = dpif->class->flow_list(dpif, flows, n);
 +    retval = dpif->dpif_class->flow_list(dpif, flows, n);
      if (retval < 0) {
          *n_out = 0;
          VLOG_WARN_RL(&error_rl, "%s: flow list failed (%s)",
@@@ -798,8 -798,7 +798,8 @@@ dpif_execute(struct dpif *dpif, uint16_
  
      COVERAGE_INC(dpif_execute);
      if (n_actions > 0) {
 -        error = dpif->class->execute(dpif, in_port, actions, n_actions, buf);
 +        error = dpif->dpif_class->execute(dpif, in_port, actions,
 +                                          n_actions, buf);
      } else {
          error = 0;
      }
  int
  dpif_recv_get_mask(const struct dpif *dpif, int *listen_mask)
  {
 -    int error = dpif->class->recv_get_mask(dpif, listen_mask);
 +    int error = dpif->dpif_class->recv_get_mask(dpif, listen_mask);
      if (error) {
          *listen_mask = 0;
      }
  int
  dpif_recv_set_mask(struct dpif *dpif, int listen_mask)
  {
 -    int error = dpif->class->recv_set_mask(dpif, listen_mask);
 +    int error = dpif->dpif_class->recv_set_mask(dpif, listen_mask);
      log_operation(dpif, "recv_set_mask", error);
      return error;
  }
  
 -    int error = (dpif->class->get_sflow_probability
 -                 ? dpif->class->get_sflow_probability(dpif, probability)
+ /* Retrieve the sFlow sampling probability.  '*probability' is expressed as the
+  * number of packets out of UINT_MAX to sample, e.g. probability/UINT_MAX is
+  * the probability of sampling a given packet.
+  *
+  * Returns 0 if successful, otherwise a positive errno value.  EOPNOTSUPP
+  * indicates that 'dpif' does not support sFlow sampling. */
+ int
+ dpif_get_sflow_probability(const struct dpif *dpif, uint32_t *probability)
+ {
 -    int error = (dpif->class->set_sflow_probability
 -                 ? dpif->class->set_sflow_probability(dpif, probability)
++    int error = (dpif->dpif_class->get_sflow_probability
++                 ? dpif->dpif_class->get_sflow_probability(dpif, probability)
+                  : EOPNOTSUPP);
+     if (error) {
+         *probability = 0;
+     }
+     log_operation(dpif, "get_sflow_probability", error);
+     return error;
+ }
+ /* Set the sFlow sampling probability.  'probability' is expressed as the
+  * number of packets out of UINT_MAX to sample, e.g. probability/UINT_MAX is
+  * the probability of sampling a given packet.
+  *
+  * Returns 0 if successful, otherwise a positive errno value.  EOPNOTSUPP
+  * indicates that 'dpif' does not support sFlow sampling. */
+ int
+ dpif_set_sflow_probability(struct dpif *dpif, uint32_t probability)
+ {
++    int error = (dpif->dpif_class->set_sflow_probability
++                 ? dpif->dpif_class->set_sflow_probability(dpif, probability)
+                  : EOPNOTSUPP);
+     log_operation(dpif, "set_sflow_probability", error);
+     return error;
+ }
  /* Attempts to receive a message from 'dpif'.  If successful, stores the
   * message into '*packetp'.  The message, if one is received, will begin with
   * 'struct odp_msg' as a header.  Only messages of the types selected with
  int
  dpif_recv(struct dpif *dpif, struct ofpbuf **packetp)
  {
 -    int error = dpif->class->recv(dpif, packetp);
 +    int error = dpif->dpif_class->recv(dpif, packetp);
      if (!error) {
          if (VLOG_IS_DBG_ENABLED()) {
              struct ofpbuf *buf = *packetp;
                          "%zu on port %"PRIu16": %s", dpif_name(dpif),
                          (msg->type == _ODPL_MISS_NR ? "miss"
                           : msg->type == _ODPL_ACTION_NR ? "action"
+                          : msg->type == _ODPL_SFLOW_NR ? "sFlow"
                           : "<unknown>"),
                          payload_len, msg->port, s);
              free(s);
@@@ -894,7 -929,7 +930,7 @@@ dpif_recv_purge(struct dpif *dpif
          return error;
      }
  
-     for (i = 0; i < stats.max_miss_queue + stats.max_action_queue; i++) {
+     for (i = 0; i < stats.max_miss_queue + stats.max_action_queue + stats.max_sflow_queue; i++) {
          struct ofpbuf *buf;
          error = dpif_recv(dpif, &buf);
          if (error) {
  void
  dpif_recv_wait(struct dpif *dpif)
  {
 -    dpif->class->recv_wait(dpif);
 +    dpif->dpif_class->recv_wait(dpif);
  }
  
  /* Obtains the NetFlow engine type and engine ID for 'dpif' into '*engine_type'
@@@ -924,11 -959,10 +960,11 @@@ dpif_get_netflow_ids(const struct dpif 
  }
  \f
  void
 -dpif_init(struct dpif *dpif, const struct dpif_class *class, const char *name,
 +dpif_init(struct dpif *dpif, const struct dpif_class *dpif_class,
 +          const char *name,
            uint8_t netflow_engine_type, uint8_t netflow_engine_id)
  {
 -    dpif->class = class;
 +    dpif->dpif_class = dpif_class;
      dpif->name = xstrdup(name);
      dpif->netflow_engine_type = netflow_engine_type;
      dpif->netflow_engine_id = netflow_engine_id;
diff --combined lib/netdev.c
@@@ -31,6 -31,7 +31,7 @@@
  #include "list.h"
  #include "netdev-provider.h"
  #include "ofpbuf.h"
+ #include "openflow/openflow.h"
  #include "packets.h"
  #include "poll-loop.h"
  #include "shash.h"
@@@ -185,7 -186,7 +186,7 @@@ netdev_destroy(const char *name
      }
  
      shash_delete(&netdev_obj_shash, node);
 -    netdev_obj->class->destroy(netdev_obj);
 +    netdev_obj->netdev_class->destroy(netdev_obj);
  
      return 0;
  }
@@@ -207,8 -208,8 +208,8 @@@ netdev_reconfigure(const char *name, co
          return ENODEV;
      }
  
 -    if (netdev_obj->class->reconfigure) {
 -        return netdev_obj->class->reconfigure(netdev_obj, args);
 +    if (netdev_obj->netdev_class->reconfigure) {
 +        return netdev_obj->netdev_class->reconfigure(netdev_obj, args);
      }
  
      return 0;
@@@ -234,7 -235,7 +235,7 @@@ netdev_open(const char *name, int ether
  
      netdev_obj = shash_find_data(&netdev_obj_shash, name);
      if (netdev_obj) {
 -        error = netdev_obj->class->open(name, ethertype, &netdev);
 +        error = netdev_obj->netdev_class->open(name, ethertype, &netdev);
      } else {
          /* Default to "system". */
          error = EAFNOSUPPORT;
@@@ -297,7 -298,7 +298,7 @@@ netdev_close(struct netdev *netdev
          }
  
          /* Free. */
 -        netdev->class->close(netdev);
 +        netdev->netdev_class->close(netdev);
          free(name);
      }
  }
@@@ -371,8 -372,8 +372,8 @@@ netdev_recv(struct netdev *netdev, stru
      assert(buffer->size == 0);
      assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN);
  
 -    retval = netdev->class->recv(netdev,
 -                                 buffer->data, ofpbuf_tailroom(buffer));
 +    retval = netdev->netdev_class->recv(netdev,
 +                                        buffer->data, ofpbuf_tailroom(buffer));
      if (retval >= 0) {
          COVERAGE_INC(netdev_received);
          buffer->size += retval;
  void
  netdev_recv_wait(struct netdev *netdev)
  {
 -    netdev->class->recv_wait(netdev);
 +    netdev->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->netdev_class->drain(netdev);
  }
  
  /* Sends 'buffer' on 'netdev'.  Returns 0 if successful, otherwise a positive
  int
  netdev_send(struct netdev *netdev, const struct ofpbuf *buffer)
  {
 -    int error = netdev->class->send(netdev, buffer->data, buffer->size);
 +    int error = netdev->netdev_class->send(netdev, buffer->data, buffer->size);
      if (!error) {
          COVERAGE_INC(netdev_sent);
      }
  void
  netdev_send_wait(struct netdev *netdev)
  {
 -    return netdev->class->send_wait(netdev);
 +    return netdev->netdev_class->send_wait(netdev);
  }
  
  /* Attempts to set 'netdev''s MAC address to 'mac'.  Returns 0 if successful,
  int
  netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN])
  {
 -    return netdev->class->set_etheraddr(netdev, mac);
 +    return netdev->netdev_class->set_etheraddr(netdev, mac);
  }
  
  /* Retrieves 'netdev''s MAC address.  If successful, returns 0 and copies the
  int
  netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN])
  {
 -    return netdev->class->get_etheraddr(netdev, mac);
 +    return netdev->netdev_class->get_etheraddr(netdev, mac);
  }
  
  /* Returns the name of the network device that 'netdev' represents,
@@@ -467,7 -468,7 +468,7 @@@ netdev_get_name(const struct netdev *ne
  int
  netdev_get_mtu(const struct netdev *netdev, int *mtup)
  {
 -    int error = netdev->class->get_mtu(netdev, mtup);
 +    int error = netdev->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));
  int
  netdev_get_ifindex(const struct netdev *netdev)
  {
 -    return netdev->class->get_ifindex(netdev);
 +    return netdev->netdev_class->get_ifindex(netdev);
  }
  
  /* Stores the features supported by 'netdev' into each of '*current',
@@@ -517,21 -518,50 +518,50 @@@ netdev_get_features(struct netdev *netd
          peer = &dummy[3];
      }
  
 -    error = netdev->class->get_features(netdev, current, advertised, supported,
 -                                        peer);
 +    error = netdev->netdev_class->get_features(netdev, current, advertised,
 +                                               supported, peer);
      if (error) {
          *current = *advertised = *supported = *peer = 0;
      }
      return error;
  }
  
+ /* Returns the maximum speed of a network connection that has the "enum
+  * ofp_port_features" bits in 'features', in bits per second.  If no bits that
+  * indicate a speed are set in 'features', assumes 100Mbps. */
+ uint64_t
+ netdev_features_to_bps(uint32_t features)
+ {
+     enum {
+         F_10000MB = OFPPF_10GB_FD,
+         F_1000MB = OFPPF_1GB_HD | OFPPF_1GB_FD,
+         F_100MB = OFPPF_100MB_HD | OFPPF_100MB_FD,
+         F_10MB = OFPPF_10MB_HD | OFPPF_10MB_FD
+     };
+     return (  features & F_10000MB  ? UINT64_C(10000000000)
+             : features & F_1000MB   ? UINT64_C(1000000000)
+             : features & F_100MB    ? UINT64_C(100000000)
+             : features & F_10MB     ? UINT64_C(10000000)
+                                     : UINT64_C(100000000));
+ }
+ /* Returns true if any of the "enum ofp_port_features" bits that indicate a
+  * full-duplex link are set in 'features', otherwise false. */
+ bool
+ netdev_features_is_full_duplex(uint32_t features)
+ {
+     return (features & (OFPPF_10MB_FD | OFPPF_100MB_FD | OFPPF_1GB_FD
+                         | OFPPF_10GB_FD)) != 0;
+ }
  /* Set the features advertised by 'netdev' to 'advertise'.  Returns 0 if
   * successful, otherwise a positive errno value. */
  int
  netdev_set_advertisements(struct netdev *netdev, uint32_t advertise)
  {
 -    return (netdev->class->set_advertisements
 -            ? netdev->class->set_advertisements(netdev, advertise)
 +    return (netdev->netdev_class->set_advertisements
 +            ? netdev->netdev_class->set_advertisements(netdev, advertise)
              : EOPNOTSUPP);
  }
  
@@@ -555,8 -585,8 +585,8 @@@ netdev_get_in4(const struct netdev *net
      struct in_addr netmask;
      int error;
  
 -    error = (netdev->class->get_in4
 -             ? netdev->class->get_in4(netdev, &address, &netmask)
 +    error = (netdev->netdev_class->get_in4
 +             ? netdev->netdev_class->get_in4(netdev, &address, &netmask)
               : EOPNOTSUPP);
      if (address_) {
          address_->s_addr = error ? 0 : address.s_addr;
  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->netdev_class->set_in4
 +            ? netdev->netdev_class->set_in4(netdev, addr, mask)
              : EOPNOTSUPP);
  }
  
@@@ -584,8 -614,8 +614,8 @@@ in
  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->netdev_class->add_router
 +            ? netdev->netdev_class->add_router(netdev, router)
              : EOPNOTSUPP);
  }
  
@@@ -601,9 -631,8 +631,9 @@@ netdev_get_next_hop(const struct netde
                      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->netdev_class->get_next_hop
 +                 ? netdev->netdev_class->get_next_hop(host, next_hop,
 +                                                      netdev_name)
                   : EOPNOTSUPP);
      if (error) {
          next_hop->s_addr = 0;
@@@ -629,8 -658,8 +659,8 @@@ netdev_get_in6(const struct netdev *net
      struct in6_addr dummy;
      int error;
  
 -    error = (netdev->class->get_in6
 -             ? netdev->class->get_in6(netdev, in6 ? in6 : &dummy)
 +    error = (netdev->netdev_class->get_in6
 +             ? netdev->netdev_class->get_in6(netdev, in6 ? in6 : &dummy)
               : EOPNOTSUPP);
      if (error && in6) {
          memset(in6, 0, sizeof *in6);
@@@ -650,8 -679,7 +680,8 @@@ do_update_flags(struct netdev *netdev, 
      enum netdev_flags old_flags;
      int error;
  
 -    error = netdev->class->update_flags(netdev, off & ~on, on, &old_flags);
 +    error = netdev->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),
@@@ -724,8 -752,8 +754,8 @@@ in
  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->netdev_class->arp_lookup
 +                 ? netdev->netdev_class->arp_lookup(netdev, ip, mac)
                   : EOPNOTSUPP);
      if (error) {
          memset(mac, 0, ETH_ADDR_LEN);
  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->netdev_class->get_carrier
 +                 ? netdev->netdev_class->get_carrier(netdev, carrier)
                   : EOPNOTSUPP);
      if (error) {
          *carrier = false;
@@@ -754,8 -782,8 +784,8 @@@ netdev_get_stats(const struct netdev *n
      int error;
  
      COVERAGE_INC(netdev_get_stats);
 -    error = (netdev->class->get_stats
 -             ? netdev->class->get_stats(netdev, stats)
 +    error = (netdev->netdev_class->get_stats
 +             ? netdev->netdev_class->get_stats(netdev, stats)
               : EOPNOTSUPP);
      if (error) {
          memset(stats, 0xff, sizeof *stats);
@@@ -770,9 -798,8 +800,9 @@@ in
  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->netdev_class->set_policing
 +            ? netdev->netdev_class->set_policing(netdev,
 +                                                 kbits_rate, kbits_burst)
              : EOPNOTSUPP);
  }
  
  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->netdev_class->get_vlan_vid
 +                 ? netdev->netdev_class->get_vlan_vid(netdev, vlan_vid)
                   : ENOENT);
      if (error) {
          *vlan_vid = 0;
@@@ -822,33 -849,32 +852,33 @@@ exit
  }
  \f
  /* Initializes 'netdev_obj' as a netdev object named 'name' of the 
 - * specified 'class'.
 + * specified 'netdev_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().  */
  void
  netdev_obj_init(struct netdev_obj *netdev_obj, const char *name,
 -                const struct netdev_class *class, bool created)
 +                const struct netdev_class *netdev_class, bool created)
  {
      assert(!shash_find(&netdev_obj_shash, name));
  
 -    netdev_obj->class = class;
 +    netdev_obj->netdev_class = netdev_class;
      netdev_obj->ref_cnt = 0;
      netdev_obj->created = created;
      shash_add(&netdev_obj_shash, name, netdev_obj);
  }
  
 -/* Initializes 'netdev' as a netdev named 'name' of the specified 'class'.
 +/* Initializes 'netdev' as a netdev named 'name' of the specified
 + * 'netdev_class'.
   *
   * 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)
 +            const struct netdev_class *netdev_class)
  {
 -    netdev->class = class;
 +    netdev->netdev_class = netdev_class;
      netdev->name = xstrdup(name);
      netdev->save_flags = 0;
      netdev->changed_flags = 0;
   * The caller must not free the returned value. */
  const char *netdev_get_type(const struct netdev *netdev)
  {
 -    return netdev->class->type;
 +    return netdev->netdev_class->type;
  }
  
  /* Initializes 'notifier' as a netdev notifier for 'netdev', for which
@@@ -900,7 -926,7 +930,7 @@@ netdev_monitor_destroy(struct netdev_mo
  
          SHASH_FOR_EACH (node, &monitor->polled_netdevs) {
              struct netdev_notifier *notifier = node->data;
 -            notifier->netdev->class->poll_remove(notifier);
 +            notifier->netdev->netdev_class->poll_remove(notifier);
          }
  
          shash_destroy(&monitor->polled_netdevs);
@@@ -930,11 -956,11 +960,11 @@@ netdev_monitor_add(struct netdev_monito
      const char *netdev_name = netdev_get_name(netdev);
      int error = 0;
      if (!shash_find(&monitor->polled_netdevs, netdev_name)
 -        && netdev->class->poll_add)
 +        && netdev->netdev_class->poll_add)
      {
          struct netdev_notifier *notifier;
 -        error = netdev->class->poll_add(netdev, netdev_monitor_cb, monitor,
 -                                        &notifier);
 +        error = netdev->netdev_class->poll_add(netdev, netdev_monitor_cb,
 +                                               monitor, &notifier);
          if (!error) {
              assert(notifier->netdev == netdev);
              shash_add(&monitor->polled_netdevs, netdev_name, notifier);
@@@ -956,7 -982,7 +986,7 @@@ netdev_monitor_remove(struct netdev_mon
      if (node) {
          /* Cancel future notifications. */
          struct netdev_notifier *notifier = node->data;
 -        netdev->class->poll_remove(notifier);
 +        netdev->netdev_class->poll_remove(notifier);
          shash_delete(&monitor->polled_netdevs, node);
  
          /* Drop any pending notification. */
@@@ -1014,10 -1040,9 +1044,10 @@@ 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,
 -                                           netdev->changed_flags & ~restore,
 -                                           restore, &old_flags);
 +        return netdev->netdev_class->update_flags(netdev,
 +                                                  netdev->changed_flags
 +                                                  & ~restore,
 +                                                  restore, &old_flags);
      }
      return 0;
  }
@@@ -353,7 -353,7 +353,7 @@@ since the switch will attempt to send t
  Disabling learning for the VLAN will cause the switch to correctly
  send the packet out all ports configured for that VLAN.  If Open
  vSwitch is being used as an intermediate switch learning can be disabled
 -by setting the key \fBvlan.\fIbrname\fB.learning-disable=\fIvid\fR
 +by setting the key \fBvlan.\fIbrname\fB.disable-learning=\fIvid\fR
  to the mirrored VLAN.
  .ST "Example"
  The following \fBovs\-vswitchd\fR configuration copies all frames received
@@@ -459,6 -459,38 +459,38 @@@ netflow.mybr.host=nflow.example.com:999
  
  .fi
  .RE
+ .SS "sFlow Monitoring"
+ sFlow(R) is a protocol for monitoring switches.  A bridge may be
+ configured to send sFlow records to sFlow collectors by defining the
+ key \fBsflow.\fIbridge\fB.host\fR for each collector in the form
+ \fIip\fR[\fB:\fIport\fR].  Records from \fIbridge\fR will be sent to
+ each \fIip\fR on UDP \fIport\fR.  The \fIip\fR must be specified
+ numerically, not as a DNS name.  If \Iport\fR is omitted, port 6343 is
+ used.
+ .PP
+ By default, 1 out of every 400 packets is sent to the configured sFlow
+ collector.  To override this, set \fBsflow.\fIbridge\fB.sampling\fR to
+ the number of switched packets out of which one, on average, will be
+ sent to the sFlow collector, e.g. a value of 1 sends every packet to
+ the collector, a value of 2 sends 50% of the packets to the collector,
+ and so on.
+ .PP
+ \fBovs\-vswitchd\fR also occasionally sends switch port statistics to
+ sFlow collectors, by default every 30 seconds.  To override this, set
+ \fBsflow.\fIbridge\fB.polling\fR to a duration in seconds.
+ .PP
+ By default, \fBovs\-vswitchd\fR sends the first 128 bytes of sampled
+ packets to sFlow collectors.  To override this, set
+ \fBsflow.\fIbridge\fB.header\fR to a size in bytes.
+ .PP
+ The sFlow module must be able to report an ``agent address'' to sFlow
+ collectors, which should be an IP address for the Open vSwitch that is
+ persistent and reachable over the network, if possible.  If a
+ local IP is configured as \fBbridge.\fIbridge\fB.controller.ip\fR,
+ then that IP address is used by default.  To override this default,
+ set \fBsflow.\fIbridge\fB.agent\fR to the name of a network device, in
+ which case the IP address set on that device is used.  If no IP
+ address can be determined either way, sFlow is disabled.
  .SS "Remote Management"
  A \fBovs\-vswitchd\fR instance may be remotely managed by a controller that
  supports the OpenFlow Management Protocol, such as NOX.  This
@@@ -514,7 -546,7 +546,7 @@@ switch will perform all configured brid
  .TP
  \fBdiscover\fR
  Use controller discovery to find the local OpenFlow controller.
- Refer to \fB\ovs\-openflowd\fR(8) for information on how to configure a DHCP
+ Refer to \fBovs\-openflowd\fR(8) for information on how to configure a DHCP
  server to support controller discovery.  The following additional
  options control the discovery process:
  .