datapath: Add support for kernel 3.14.
[sliver-openvswitch.git] / datapath / vport.c
index 2673b81..0dcecd0 100644 (file)
@@ -136,18 +136,20 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
 
        vport->dp = parms->dp;
        vport->port_no = parms->port_no;
-       vport->upcall_portid = parms->upcall_portid;
        vport->ops = ops;
        INIT_HLIST_NODE(&vport->dp_hash_node);
 
-       vport->percpu_stats = alloc_percpu(struct pcpu_tstats);
+       if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids))
+               return ERR_PTR(-EINVAL);
+
+       vport->percpu_stats = alloc_percpu(struct pcpu_sw_netstats);
        if (!vport->percpu_stats) {
                kfree(vport);
                return ERR_PTR(-ENOMEM);
        }
 
        for_each_possible_cpu(i) {
-               struct pcpu_tstats *vport_stats;
+               struct pcpu_sw_netstats *vport_stats;
                vport_stats = per_cpu_ptr(vport->percpu_stats, i);
                u64_stats_init(&vport_stats->syncp);
        }
@@ -169,6 +171,7 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
  */
 void ovs_vport_free(struct vport *vport)
 {
+       kfree((struct vport_portids __force *)vport->upcall_portids);
        free_percpu(vport->percpu_stats);
        kfree(vport);
 }
@@ -299,8 +302,8 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
        spin_unlock_bh(&vport->stats_lock);
 
        for_each_possible_cpu(i) {
-               const struct pcpu_tstats *percpu_stats;
-               struct pcpu_tstats local_stats;
+               const struct pcpu_sw_netstats *percpu_stats;
+               struct pcpu_sw_netstats local_stats;
                unsigned int start;
 
                percpu_stats = per_cpu_ptr(vport->percpu_stats, i);
@@ -355,6 +358,103 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
        return 0;
 }
 
+static void vport_portids_destroy_rcu_cb(struct rcu_head *rcu)
+{
+       struct vport_portids *ids = container_of(rcu, struct vport_portids,
+                                                rcu);
+
+       kfree(ids);
+}
+
+/**
+ *     ovs_vport_set_upcall_portids - set upcall portids of @vport.
+ *
+ * @vport: vport to modify.
+ * @ids: new configuration, an array of port ids.
+ *
+ * Sets the vport's upcall_portids to @ids.
+ *
+ * Returns 0 if successful, -EINVAL if @ids is zero length or cannot be parsed
+ * as an array of U32.
+ *
+ * Must be called with ovs_mutex.
+ */
+int ovs_vport_set_upcall_portids(struct vport *vport,  struct nlattr *ids)
+{
+       struct vport_portids *old, *vport_portids;
+
+       if (!nla_len(ids) || nla_len(ids) % sizeof(u32))
+               return -EINVAL;
+
+       old = ovsl_dereference(vport->upcall_portids);
+
+       vport_portids = kmalloc(sizeof *vport_portids + nla_len(ids),
+                               GFP_KERNEL);
+       vport_portids->n_ids = nla_len(ids) / sizeof(u32);
+       vport_portids->rn_ids = reciprocal_value(vport_portids->n_ids);
+       nla_memcpy(vport_portids->ids, ids, nla_len(ids));
+
+       rcu_assign_pointer(vport->upcall_portids, vport_portids);
+
+       if (old)
+               call_rcu(&old->rcu, vport_portids_destroy_rcu_cb);
+
+       return 0;
+}
+
+/**
+ *     ovs_vport_get_upcall_portids - get the upcall_portids of @vport.
+ *
+ * @vport: vport from which to retrieve the portids.
+ * @skb: sk_buff where portids should be appended.
+ *
+ * Retrieves the configuration of the given vport, appending the
+ * %OVS_VPORT_ATTR_UPCALL_PID attribute which is the array of upcall
+ * portids to @skb.
+ *
+ * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room.
+ * If an error occurs, @skb is left unmodified.  Must be called with
+ * ovs_mutex or rcu_read_lock.
+ */
+int ovs_vport_get_upcall_portids(const struct vport *vport,
+                                struct sk_buff *skb)
+{
+       struct vport_portids *ids;
+
+       ids = rcu_dereference_ovsl(vport->upcall_portids);
+
+       if (vport->dp->user_features & OVS_DP_F_VPORT_PIDS)
+               return nla_put(skb, OVS_VPORT_ATTR_UPCALL_PID,
+                              ids->n_ids * sizeof(u32), (void *) ids->ids);
+       else
+               return nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->ids[0]);
+}
+
+/**
+ *     ovs_vport_find_upcall_portid - find the upcall portid to send upcall.
+ *
+ * @vport: vport from which the missed packet is received.
+ * @skb: skb that the missed packet was received.
+ *
+ * Uses the skb_get_hash() to select the upcall portid to send the
+ * upcall.
+ *
+ * Returns the portid of the target socket.  Must be called with rcu_read_lock.
+ */
+u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb)
+{
+       struct vport_portids *ids;
+       u32 hash;
+
+       ids = rcu_dereference(p->upcall_portids);
+
+       if (ids->n_ids == 1 && ids->ids[0] == 0)
+               return 0;
+
+       hash = skb_get_hash(skb);
+       return ids->ids[hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids)];
+}
+
 /**
  *     ovs_vport_receive - pass up received packet to the datapath for processing
  *
@@ -369,7 +469,7 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
 void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
                       struct ovs_key_ipv4_tunnel *tun_key)
 {
-       struct pcpu_tstats *stats;
+       struct pcpu_sw_netstats *stats;
 
        stats = this_cpu_ptr(vport->percpu_stats);
        u64_stats_update_begin(&stats->syncp);
@@ -395,7 +495,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
        int sent = vport->ops->send(vport, skb);
 
        if (likely(sent > 0)) {
-               struct pcpu_tstats *stats;
+               struct pcpu_sw_netstats *stats;
 
                stats = this_cpu_ptr(vport->percpu_stats);