datapath: Change userspace vport interface to use Netlink attributes.
[sliver-openvswitch.git] / datapath / vport.c
index ee80645..8289c01 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 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
@@ -136,26 +136,6 @@ error:
        return err;
 }
 
-static void vport_del_all(void)
-{
-       int i;
-
-       rtnl_lock();
-       vport_lock();
-
-       for (i = 0; i < VPORT_HASH_BUCKETS; i++) {
-               struct hlist_head *bucket = &dev_table[i];
-               struct vport *vport;
-               struct hlist_node *node, *next;
-
-               hlist_for_each_entry_safe(vport, node, next, bucket, hash_node)
-                       vport_del(vport);
-       }
-
-       vport_unlock();
-       rtnl_unlock();
-}
-
 /**
  *     vport_exit - shutdown vport subsystem
  *
@@ -166,8 +146,6 @@ void vport_exit(void)
 {
        int i;
 
-       vport_del_all();
-
        for (i = 0; i < n_vport_types; i++) {
                if (vport_ops_list[i]->exit)
                        vport_ops_list[i]->exit();
@@ -177,277 +155,6 @@ void vport_exit(void)
        kfree(dev_table);
 }
 
-/**
- *     vport_user_mod - modify existing vport device (for userspace callers)
- *
- * @uport: New configuration for vport
- *
- * Modifies an existing device with the specified configuration (which is
- * dependent on device type).  This function is for userspace callers and
- * assumes no locks are held.
- */
-int vport_user_mod(const struct odp_port __user *uport)
-{
-       struct odp_port port;
-       struct vport *vport;
-       int err;
-
-       if (copy_from_user(&port, uport, sizeof(port)))
-               return -EFAULT;
-
-       port.devname[IFNAMSIZ - 1] = '\0';
-
-       rtnl_lock();
-
-       vport = vport_locate(port.devname);
-       if (!vport) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       vport_lock();
-       err = vport_mod(vport, &port);
-       vport_unlock();
-
-out:
-       rtnl_unlock();
-       return err;
-}
-
-/**
- *     vport_user_stats_get - retrieve device stats (for userspace callers)
- *
- * @ustats_req: Stats request parameters.
- *
- * Retrieves transmit, receive, and error stats for the given device.  This
- * function is for userspace callers and assumes no locks are held.
- */
-int vport_user_stats_get(struct odp_vport_stats_req __user *ustats_req)
-{
-       struct odp_vport_stats_req stats_req;
-       struct vport *vport;
-       int err;
-
-       if (copy_from_user(&stats_req, ustats_req, sizeof(struct odp_vport_stats_req)))
-               return -EFAULT;
-
-       stats_req.devname[IFNAMSIZ - 1] = '\0';
-
-       vport_lock();
-
-       vport = vport_locate(stats_req.devname);
-       if (!vport) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       err = vport_get_stats(vport, &stats_req.stats);
-
-out:
-       vport_unlock();
-
-       if (!err)
-               if (copy_to_user(ustats_req, &stats_req, sizeof(struct odp_vport_stats_req)))
-                       err = -EFAULT;
-
-       return err;
-}
-
-/**
- *     vport_user_stats_set - sets offset device stats (for userspace callers)
- *
- * @ustats_req: Stats set parameters.
- *
- * Provides a set of transmit, receive, and error stats to be added as an
- * offset to the collect data when stats are retreived.  Some devices may not
- * support setting the stats, in which case the result will always be
- * -EOPNOTSUPP.  This function is for userspace callers and assumes no locks
- * are held.
- */
-int vport_user_stats_set(struct odp_vport_stats_req __user *ustats_req)
-{
-       struct odp_vport_stats_req stats_req;
-       struct vport *vport;
-       int err;
-
-       if (copy_from_user(&stats_req, ustats_req, sizeof(struct odp_vport_stats_req)))
-               return -EFAULT;
-
-       stats_req.devname[IFNAMSIZ - 1] = '\0';
-
-       rtnl_lock();
-       vport_lock();
-
-       vport = vport_locate(stats_req.devname);
-       if (!vport) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       err = vport_set_stats(vport, &stats_req.stats);
-
-out:
-       vport_unlock();
-       rtnl_unlock();
-       return err;
-}
-
-
-/**
- *     vport_user_ether_get - retrieve device Ethernet address (for userspace callers)
- *
- * @uvport_ether: Ethernet address request parameters.
- *
- * Retrieves the Ethernet address of the given device.  This function is for
- * userspace callers and assumes no locks are held.
- */
-int vport_user_ether_get(struct odp_vport_ether __user *uvport_ether)
-{
-       struct odp_vport_ether vport_ether;
-       struct vport *vport;
-       int err = 0;
-
-       if (copy_from_user(&vport_ether, uvport_ether, sizeof(struct odp_vport_ether)))
-               return -EFAULT;
-
-       vport_ether.devname[IFNAMSIZ - 1] = '\0';
-
-       vport_lock();
-
-       vport = vport_locate(vport_ether.devname);
-       if (!vport) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       rcu_read_lock();
-       memcpy(vport_ether.ether_addr, vport_get_addr(vport), ETH_ALEN);
-       rcu_read_unlock();
-
-out:
-       vport_unlock();
-
-       if (!err)
-               if (copy_to_user(uvport_ether, &vport_ether, sizeof(struct odp_vport_ether)))
-                       err = -EFAULT;
-
-       return err;
-}
-
-/**
- *     vport_user_ether_set - set device Ethernet address (for userspace callers)
- *
- * @uvport_ether: Ethernet address request parameters.
- *
- * Sets the Ethernet address of the given device.  Some devices may not support
- * setting the Ethernet address, in which case the result will always be
- * -EOPNOTSUPP.  This function is for userspace callers and assumes no locks
- * are held.
- */
-int vport_user_ether_set(struct odp_vport_ether __user *uvport_ether)
-{
-       struct odp_vport_ether vport_ether;
-       struct vport *vport;
-       int err;
-
-       if (copy_from_user(&vport_ether, uvport_ether, sizeof(struct odp_vport_ether)))
-               return -EFAULT;
-
-       vport_ether.devname[IFNAMSIZ - 1] = '\0';
-
-       rtnl_lock();
-       vport_lock();
-
-       vport = vport_locate(vport_ether.devname);
-       if (!vport) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       err = vport_set_addr(vport, vport_ether.ether_addr);
-
-out:
-       vport_unlock();
-       rtnl_unlock();
-       return err;
-}
-
-/**
- *     vport_user_mtu_get - retrieve device MTU (for userspace callers)
- *
- * @uvport_mtu: MTU request parameters.
- *
- * Retrieves the MTU of the given device.  This function is for userspace
- * callers and assumes no locks are held.
- */
-int vport_user_mtu_get(struct odp_vport_mtu __user *uvport_mtu)
-{
-       struct odp_vport_mtu vport_mtu;
-       struct vport *vport;
-       int err = 0;
-
-       if (copy_from_user(&vport_mtu, uvport_mtu, sizeof(struct odp_vport_mtu)))
-               return -EFAULT;
-
-       vport_mtu.devname[IFNAMSIZ - 1] = '\0';
-
-       vport_lock();
-
-       vport = vport_locate(vport_mtu.devname);
-       if (!vport) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       vport_mtu.mtu = vport_get_mtu(vport);
-
-out:
-       vport_unlock();
-
-       if (!err)
-               if (copy_to_user(uvport_mtu, &vport_mtu, sizeof(struct odp_vport_mtu)))
-                       err = -EFAULT;
-
-       return err;
-}
-
-/**
- *     vport_user_mtu_set - set device MTU (for userspace callers)
- *
- * @uvport_mtu: MTU request parameters.
- *
- * Sets the MTU of the given device.  Some devices may not support setting the
- * MTU, in which case the result will always be -EOPNOTSUPP.  This function is
- * for userspace callers and assumes no locks are held.
- */
-int vport_user_mtu_set(struct odp_vport_mtu __user *uvport_mtu)
-{
-       struct odp_vport_mtu vport_mtu;
-       struct vport *vport;
-       int err;
-
-       if (copy_from_user(&vport_mtu, uvport_mtu, sizeof(struct odp_vport_mtu)))
-               return -EFAULT;
-
-       vport_mtu.devname[IFNAMSIZ - 1] = '\0';
-
-       rtnl_lock();
-       vport_lock();
-
-       vport = vport_locate(vport_mtu.devname);
-       if (!vport) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       err = vport_set_mtu(vport, vport_mtu.mtu);
-
-out:
-       vport_unlock();
-       rtnl_unlock();
-       return err;
-}
-
 static struct hlist_head *hash_bucket(const char *name)
 {
        unsigned int hash = full_name_hash(name, strlen(name));
@@ -577,8 +284,9 @@ void vport_free(struct vport *vport)
  *
  * @parms: Information about new vport.
  *
- * Creates a new vport with the specified configuration (which is dependent
- * on device type).  Both RTNL and vport locks must be held.
+ * Creates a new vport with the specified configuration (which is dependent on
+ * device type) and attaches it to a datapath.  Both RTNL and vport locks must
+ * be held.
  */
 struct vport *vport_add(const struct vport_parms *parms)
 {
@@ -590,7 +298,7 @@ struct vport *vport_add(const struct vport_parms *parms)
        ASSERT_VPORT();
 
        for (i = 0; i < n_vport_types; i++) {
-               if (!strcmp(vport_ops_list[i]->type, parms->type)) {
+               if (vport_ops_list[i]->type == parms->type) {
                        vport = vport_ops_list[i]->create(parms);
                        if (IS_ERR(vport)) {
                                err = PTR_ERR(vport);
@@ -609,23 +317,21 @@ out:
 }
 
 /**
- *     vport_mod - modify existing vport device (for kernel callers)
+ *     vport_set_options - modify existing vport device (for kernel callers)
  *
  * @vport: vport to modify.
  * @port: New configuration.
  *
  * Modifies an existing device with the specified configuration (which is
- * dependent on device type).  Both RTNL and vport locks must be held.
+ * dependent on device type).  RTNL lock must be held.
  */
-int vport_mod(struct vport *vport, struct odp_port *port)
+int vport_set_options(struct vport *vport, struct nlattr *options)
 {
        ASSERT_RTNL();
-       ASSERT_VPORT();
 
-       if (vport->ops->modify)
-               return vport->ops->modify(vport, port);
-       else
+       if (!vport->ops->set_options)
                return -EOPNOTSUPP;
+       return vport->ops->set_options(vport, options);
 }
 
 /**
@@ -633,9 +339,8 @@ int vport_mod(struct vport *vport, struct odp_port *port)
  *
  * @vport: vport to delete.
  *
- * Deletes the specified device.  The device must not be currently attached to
- * a datapath.  It is possible to fail for reasons such as lack of memory.
- * Both RTNL and vport locks must be held.
+ * Detaches @vport from its datapath and destroys it.  It is possible to fail
+ * for reasons such as lack of memory.  Both RTNL and vport locks must be held.
  */
 int vport_del(struct vport *vport)
 {
@@ -647,42 +352,6 @@ int vport_del(struct vport *vport)
        return vport->ops->destroy(vport);
 }
 
-/**
- *     vport_attach - notify a vport that it has been attached to a datapath
- *
- * @vport: vport to attach.
- *
- * Performs vport-specific actions so that packets may be exchanged.  RTNL lock
- * and the appropriate DP mutex must be held.
- */
-int vport_attach(struct vport *vport)
-{
-       ASSERT_RTNL();
-
-       if (vport->ops->attach)
-               return vport->ops->attach(vport);
-
-       return 0;
-}
-
-/**
- *     vport_detach - detach a vport from a datapath
- *
- * @vport: vport to detach.
- *
- * Performs vport-specific actions before a vport is detached from a datapath.
- * May fail for a variety of reasons, including lack of memory.  RTNL lock and
- * the appropriate DP mutex must be held.
- */
-int vport_detach(struct vport *vport)
-{
-       ASSERT_RTNL();
-
-       if (vport->ops->detach)
-               return vport->ops->detach(vport);
-       return 0;
-}
-
 /**
  *     vport_set_mtu - set device MTU (for kernel callers)
  *
@@ -757,9 +426,7 @@ int vport_set_stats(struct vport *vport, struct rtnl_link_stats64 *stats)
                spin_unlock_bh(&vport->stats_lock);
 
                return 0;
-       } else if (vport->ops->set_stats)
-               return vport->ops->set_stats(vport, stats);
-       else
+       } else
                return -EOPNOTSUPP;
 }
 
@@ -782,9 +449,9 @@ const char *vport_get_name(const struct vport *vport)
  * @vport: vport from which to retrieve the type.
  *
  * Retrieves the type of the given device.  Either RTNL lock or rcu_read_lock
- * must be held for the entire duration that the type is in use.
+ * must be held.
  */
-const char *vport_get_type(const struct vport *vport)
+enum odp_vport_type vport_get_type(const struct vport *vport)
 {
        return vport->ops->type;
 }
@@ -819,6 +486,17 @@ struct kobject *vport_get_kobj(const struct vport *vport)
                return NULL;
 }
 
+static int vport_call_get_stats(struct vport *vport, struct rtnl_link_stats64 *stats)
+{
+       int err;
+
+       rcu_read_lock();
+       err = vport->ops->get_stats(vport, stats);
+       rcu_read_unlock();
+
+       return err;
+}
+
 /**
  *     vport_get_stats - retrieve device stats (for kernel callers)
  *
@@ -829,95 +507,77 @@ struct kobject *vport_get_kobj(const struct vport *vport)
  */
 int vport_get_stats(struct vport *vport, struct rtnl_link_stats64 *stats)
 {
-       struct rtnl_link_stats64 dev_stats;
-       struct rtnl_link_stats64 *dev_statsp = NULL;
-       int err;
-
-       if (vport->ops->get_stats) {
-               if (vport->ops->flags & VPORT_F_GEN_STATS)
-                       dev_statsp = &dev_stats;
-               else
-                       dev_statsp = stats;
-
-               rcu_read_lock();
-               err = vport->ops->get_stats(vport, dev_statsp);
-               rcu_read_unlock();
-
-               if (err)
-                       goto out;
-       }
+       int i;
 
-       if (vport->ops->flags & VPORT_F_GEN_STATS) {
-               int i;
+       if (!(vport->ops->flags & VPORT_F_GEN_STATS))
+               return vport_call_get_stats(vport, stats);
 
-               /* We potentially have 3 sources of stats that need to be
-                * combined: those we have collected (split into err_stats and
-                * percpu_stats), offset_stats from set_stats(), and device
-                * error stats from get_stats() (for errors that happen
-                * downstream and therefore aren't reported through our
-                * vport_record_error() function). */
+       /* We potentially have 3 sources of stats that need to be
+        * combined: those we have collected (split into err_stats and
+        * percpu_stats), offset_stats from set_stats(), and device
+        * error stats from get_stats() (for errors that happen
+        * downstream and therefore aren't reported through our
+        * vport_record_error() function). */
 
-               spin_lock_bh(&vport->stats_lock);
+       spin_lock_bh(&vport->stats_lock);
 
-               *stats = vport->offset_stats;
+       *stats = vport->offset_stats;
 
-               stats->rx_errors        += vport->err_stats.rx_errors;
-               stats->tx_errors        += vport->err_stats.tx_errors;
-               stats->tx_dropped       += vport->err_stats.tx_dropped;
-               stats->rx_dropped       += vport->err_stats.rx_dropped;
+       stats->rx_errors        += vport->err_stats.rx_errors;
+       stats->tx_errors        += vport->err_stats.tx_errors;
+       stats->tx_dropped       += vport->err_stats.tx_dropped;
+       stats->rx_dropped       += vport->err_stats.rx_dropped;
 
-               spin_unlock_bh(&vport->stats_lock);
+       spin_unlock_bh(&vport->stats_lock);
 
-               if (dev_statsp) {
-                       stats->rx_packets          += dev_statsp->rx_packets;
-                       stats->tx_packets          += dev_statsp->tx_packets;
-                       stats->rx_bytes            += dev_statsp->rx_bytes;
-                       stats->tx_bytes            += dev_statsp->tx_bytes;
-                       stats->rx_errors           += dev_statsp->rx_errors;
-                       stats->tx_errors           += dev_statsp->tx_errors;
-                       stats->rx_dropped          += dev_statsp->rx_dropped;
-                       stats->tx_dropped          += dev_statsp->tx_dropped;
-                       stats->multicast           += dev_statsp->multicast;
-                       stats->collisions          += dev_statsp->collisions;
-                       stats->rx_length_errors    += dev_statsp->rx_length_errors;
-                       stats->rx_over_errors      += dev_statsp->rx_over_errors;
-                       stats->rx_crc_errors       += dev_statsp->rx_crc_errors;
-                       stats->rx_frame_errors     += dev_statsp->rx_frame_errors;
-                       stats->rx_fifo_errors      += dev_statsp->rx_fifo_errors;
-                       stats->rx_missed_errors    += dev_statsp->rx_missed_errors;
-                       stats->tx_aborted_errors   += dev_statsp->tx_aborted_errors;
-                       stats->tx_carrier_errors   += dev_statsp->tx_carrier_errors;
-                       stats->tx_fifo_errors      += dev_statsp->tx_fifo_errors;
-                       stats->tx_heartbeat_errors += dev_statsp->tx_heartbeat_errors;
-                       stats->tx_window_errors    += dev_statsp->tx_window_errors;
-                       stats->rx_compressed       += dev_statsp->rx_compressed;
-                       stats->tx_compressed       += dev_statsp->tx_compressed;
-               }
+       if (vport->ops->get_stats) {
+               struct rtnl_link_stats64 dev_stats;
+               int err;
 
-               for_each_possible_cpu(i) {
-                       const struct vport_percpu_stats *percpu_stats;
-                       struct vport_percpu_stats local_stats;
-                       unsigned seqcount;
+               err = vport_call_get_stats(vport, &dev_stats);
+               if (err)
+                       return err;
+
+               stats->rx_errors           += dev_stats.rx_errors;
+               stats->tx_errors           += dev_stats.tx_errors;
+               stats->rx_dropped          += dev_stats.rx_dropped;
+               stats->tx_dropped          += dev_stats.tx_dropped;
+               stats->multicast           += dev_stats.multicast;
+               stats->collisions          += dev_stats.collisions;
+               stats->rx_length_errors    += dev_stats.rx_length_errors;
+               stats->rx_over_errors      += dev_stats.rx_over_errors;
+               stats->rx_crc_errors       += dev_stats.rx_crc_errors;
+               stats->rx_frame_errors     += dev_stats.rx_frame_errors;
+               stats->rx_fifo_errors      += dev_stats.rx_fifo_errors;
+               stats->rx_missed_errors    += dev_stats.rx_missed_errors;
+               stats->tx_aborted_errors   += dev_stats.tx_aborted_errors;
+               stats->tx_carrier_errors   += dev_stats.tx_carrier_errors;
+               stats->tx_fifo_errors      += dev_stats.tx_fifo_errors;
+               stats->tx_heartbeat_errors += dev_stats.tx_heartbeat_errors;
+               stats->tx_window_errors    += dev_stats.tx_window_errors;
+               stats->rx_compressed       += dev_stats.rx_compressed;
+               stats->tx_compressed       += dev_stats.tx_compressed;
+       }
 
-                       percpu_stats = per_cpu_ptr(vport->percpu_stats, i);
+       for_each_possible_cpu(i) {
+               const struct vport_percpu_stats *percpu_stats;
+               struct vport_percpu_stats local_stats;
+               unsigned seqcount;
 
-                       do {
-                               seqcount = read_seqcount_begin(&percpu_stats->seqlock);
-                               local_stats = *percpu_stats;
-                       } while (read_seqcount_retry(&percpu_stats->seqlock, seqcount));
+               percpu_stats = per_cpu_ptr(vport->percpu_stats, i);
 
-                       stats->rx_bytes         += local_stats.rx_bytes;
-                       stats->rx_packets       += local_stats.rx_packets;
-                       stats->tx_bytes         += local_stats.tx_bytes;
-                       stats->tx_packets       += local_stats.tx_packets;
-               }
+               do {
+                       seqcount = read_seqcount_begin(&percpu_stats->seqlock);
+                       local_stats = *percpu_stats;
+               } while (read_seqcount_retry(&percpu_stats->seqlock, seqcount));
 
-               err = 0;
-       } else
-               err = -EOPNOTSUPP;
+               stats->rx_bytes         += local_stats.rx_bytes;
+               stats->rx_packets       += local_stats.rx_packets;
+               stats->tx_bytes         += local_stats.tx_bytes;
+               stats->tx_packets       += local_stats.tx_packets;
+       }
 
-out:
-       return err;
+       return 0;
 }
 
 /**
@@ -964,19 +624,17 @@ unsigned char vport_get_operstate(const struct vport *vport)
  *
  * @vport: vport from which to retrieve index
  *
- * Retrieves the system interface index of the given device.  Not all devices
- * will have system indexes, in which case the index of the datapath local
- * port is returned.  Returns a negative index on error.  Either RTNL lock or
+ * Retrieves the system interface index of the given device or 0 if
+ * the device does not have one (in the case of virtual ports).
+ * Returns a negative index on error. Either RTNL lock or
  * rcu_read_lock must be held.
  */
 int vport_get_ifindex(const struct vport *vport)
 {
        if (vport->ops->get_ifindex)
                return vport->ops->get_ifindex(vport);
-
-       /* If we don't actually have an ifindex, use the local port's.
-        * Userspace doesn't check it anyways. */
-       return vport_get_ifindex(vport->dp->ports[ODPP_LOCAL]);
+       else
+               return 0;
 }
 
 /**
@@ -1013,6 +671,38 @@ int vport_get_mtu(const struct vport *vport)
        return vport->ops->get_mtu(vport);
 }
 
+/**
+ *     vport_get_options - retrieve device options
+ *
+ * @vport: vport from which to retrieve the options.
+ * @skb: sk_buff where options should be appended.
+ *
+ * Retrieves the configuration of the given device, appending an
+ * %ODP_VPORT_ATTR_OPTIONS attribute that in turn contains nested
+ * vport-specific attributes to @skb.  Either RTNL lock or rcu_read_lock must
+ * be held.
+ *
+ * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room, or another
+ * negative error code if a real error occurred.
+ */
+int vport_get_options(const struct vport *vport, struct sk_buff *skb)
+{
+       struct nlattr *nla;
+
+       nla = nla_nest_start(skb, ODP_VPORT_ATTR_OPTIONS);
+       if (!nla)
+               return -EMSGSIZE;
+
+       if (vport->ops->get_options) {
+               int err = vport->ops->get_options(vport, skb);
+               if (err)
+                       return err;
+       }
+
+       nla_nest_end(skb, nla);
+       return 0;
+}
+
 /**
  *     vport_receive - pass up received packet to the datapath for processing
  *