datapath: Replace __force type cast with rcu_dereference_raw().
[cascardo/ovs.git] / datapath / vport.c
index 42ee726..18f3956 100644 (file)
@@ -82,7 +82,7 @@ void ovs_vport_exit(void)
        kfree(dev_table);
 }
 
-static struct hlist_head *hash_bucket(struct net *net, const char *name)
+static struct hlist_head *hash_bucket(const struct net *net, const char *name)
 {
        unsigned int hash = jhash(name, strlen(name), (unsigned long) net);
        return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];
@@ -95,7 +95,7 @@ static struct hlist_head *hash_bucket(struct net *net, const char *name)
  *
  * Must be called with ovs or RCU read lock.
  */
-struct vport *ovs_vport_locate(struct net *net, const char *name)
+struct vport *ovs_vport_locate(const struct net *net, const char *name)
 {
        struct hlist_head *bucket = hash_bucket(net, name);
        struct vport *vport;
@@ -124,7 +124,6 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
 {
        struct vport *vport;
        size_t alloc_size;
-       int i;
 
        alloc_size = sizeof(struct vport);
        if (priv_size) {
@@ -141,23 +140,17 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
        vport->ops = ops;
        INIT_HLIST_NODE(&vport->dp_hash_node);
 
-       if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids))
+       if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids)) {
+               kfree(vport);
                return ERR_PTR(-EINVAL);
+       }
 
-       vport->percpu_stats = alloc_percpu(struct pcpu_sw_netstats);
+       vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
        if (!vport->percpu_stats) {
                kfree(vport);
                return ERR_PTR(-ENOMEM);
        }
 
-       for_each_possible_cpu(i) {
-               struct pcpu_sw_netstats *vport_stats;
-               vport_stats = per_cpu_ptr(vport->percpu_stats, i);
-               u64_stats_init(&vport_stats->syncp);
-       }
-
-       spin_lock_init(&vport->stats_lock);
-
        return vport;
 }
 
@@ -173,7 +166,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);
+       kfree(rcu_dereference_raw(vport->upcall_portids));
        free_percpu(vport->percpu_stats);
        kfree(vport);
 }
@@ -247,26 +240,6 @@ void ovs_vport_del(struct vport *vport)
        vport->ops->destroy(vport);
 }
 
-/**
- *     ovs_vport_set_stats - sets offset device stats
- *
- * @vport: vport on which to set stats
- * @stats: stats to set
- *
- * Provides a set of transmit, receive, and error stats to be added as an
- * offset to the collected data when stats are retrieved.  Some devices may not
- * support setting the stats, in which case the result will always be
- * -EOPNOTSUPP.
- *
- * Must be called with ovs_mutex.
- */
-void ovs_vport_set_stats(struct vport *vport, struct ovs_vport_stats *stats)
-{
-       spin_lock_bh(&vport->stats_lock);
-       vport->offset_stats = *stats;
-       spin_unlock_bh(&vport->stats_lock);
-}
-
 /**
  *     ovs_vport_get_stats - retrieve device stats
  *
@@ -281,27 +254,25 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
 {
        int i;
 
-       /* We potentially have 3 sources of stats that need to be
+       /* We potentially have two surces 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 netdev->get_stats() (for errors that happen
-        * downstream and therefore aren't reported through our
-        * vport_record_error() function).
-        * Stats from first two sources are merged and reported by ovs over
+        * percpu_stats), and device error stats from netdev->get_stats()
+        * (for errors that happen downstream and therefore aren't
+        * reported through our vport_record_error() function).
+        * Stats from first source are reported by ovs over
         * OVS_VPORT_ATTR_STATS.
         * netdev-stats can be directly read over netlink-ioctl.
         */
 
-       spin_lock_bh(&vport->stats_lock);
+       stats->rx_errors  = atomic_long_read(&vport->err_stats.rx_errors);
+       stats->tx_errors  = atomic_long_read(&vport->err_stats.tx_errors);
+       stats->tx_dropped = atomic_long_read(&vport->err_stats.tx_dropped);
+       stats->rx_dropped = atomic_long_read(&vport->err_stats.rx_dropped);
 
-       *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;
-
-       spin_unlock_bh(&vport->stats_lock);
+       stats->rx_bytes         = 0;
+       stats->rx_packets       = 0;
+       stats->tx_bytes         = 0;
+       stats->tx_packets       = 0;
 
        for_each_possible_cpu(i) {
                const struct pcpu_sw_netstats *percpu_stats;
@@ -381,7 +352,7 @@ static void vport_portids_destroy_rcu_cb(struct rcu_head *rcu)
  *
  * Must be called with ovs_mutex.
  */
-int ovs_vport_set_upcall_portids(struct vport *vport,  struct nlattr *ids)
+int ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids)
 {
        struct vport_portids *old, *vport_portids;
 
@@ -392,6 +363,9 @@ int ovs_vport_set_upcall_portids(struct vport *vport,  struct nlattr *ids)
 
        vport_portids = kmalloc(sizeof *vport_portids + nla_len(ids),
                                GFP_KERNEL);
+       if (!vport_portids)
+               return -ENOMEM;
+
        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));
@@ -443,12 +417,12 @@ int ovs_vport_get_upcall_portids(const struct vport *vport,
  *
  * 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)
+u32 ovs_vport_find_upcall_portid(const struct vport *vport, struct sk_buff *skb)
 {
        struct vport_portids *ids;
        u32 hash;
 
-       ids = rcu_dereference(p->upcall_portids);
+       ids = rcu_dereference(vport->upcall_portids);
 
        if (ids->n_ids == 1 && ids->ids[0] == 0)
                return 0;
@@ -469,9 +443,11 @@ u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb)
  * called compute_ip_summed() to initialize the checksumming fields.
  */
 void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
-                      struct ovs_tunnel_info *tun_info)
+                      const struct ovs_tunnel_info *tun_info)
 {
        struct pcpu_sw_netstats *stats;
+       struct sw_flow_key key;
+       int error;
 
        stats = this_cpu_ptr(vport->percpu_stats);
        u64_stats_update_begin(&stats->syncp);
@@ -480,9 +456,15 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
        u64_stats_update_end(&stats->syncp);
 
        ovs_skb_init_inner_protocol(skb);
-       OVS_CB(skb)->tun_info = tun_info;
        OVS_CB(skb)->input_vport = vport;
-       ovs_dp_process_received_packet(skb);
+       OVS_CB(skb)->egress_tun_info = NULL;
+       error = ovs_flow_key_extract(tun_info, skb, &key);
+       if (unlikely(error)) {
+               kfree_skb(skb);
+               return;
+       }
+
+       ovs_dp_process_packet(skb, &key);
 }
 
 /**
@@ -528,27 +510,23 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
 static void ovs_vport_record_error(struct vport *vport,
                                   enum vport_err_type err_type)
 {
-       spin_lock(&vport->stats_lock);
-
        switch (err_type) {
        case VPORT_E_RX_DROPPED:
-               vport->err_stats.rx_dropped++;
+               atomic_long_inc(&vport->err_stats.rx_dropped);
                break;
 
        case VPORT_E_RX_ERROR:
-               vport->err_stats.rx_errors++;
+               atomic_long_inc(&vport->err_stats.rx_errors);
                break;
 
        case VPORT_E_TX_DROPPED:
-               vport->err_stats.tx_dropped++;
+               atomic_long_inc(&vport->err_stats.tx_dropped);
                break;
 
        case VPORT_E_TX_ERROR:
-               vport->err_stats.tx_errors++;
+               atomic_long_inc(&vport->err_stats.tx_errors);
                break;
        }
-
-       spin_unlock(&vport->stats_lock);
 }
 
 static void free_vport_rcu(struct rcu_head *rcu)
@@ -565,3 +543,61 @@ void ovs_vport_deferred_free(struct vport *vport)
 
        call_rcu(&vport->rcu, free_vport_rcu);
 }
+
+int ovs_tunnel_get_egress_info(struct ovs_tunnel_info *egress_tun_info,
+                              struct net *net,
+                              const struct ovs_tunnel_info *tun_info,
+                              u8 ipproto,
+                              u32 skb_mark,
+                              __be16 tp_src,
+                              __be16 tp_dst)
+{
+       const struct ovs_key_ipv4_tunnel *tun_key;
+       struct rtable *rt;
+       __be32 saddr;
+
+       if (unlikely(!tun_info))
+               return -EINVAL;
+
+       tun_key = &tun_info->tunnel;
+       saddr = tun_key->ipv4_src;
+       /* Route lookup to get srouce IP address: saddr.
+        * The process may need to be changed if the corresponding process
+        * in vports ops changed.
+        */
+       rt = find_route(net,
+                       &saddr,
+                       tun_key->ipv4_dst,
+                       ipproto,
+                       tun_key->ipv4_tos,
+                       skb_mark);
+       if (IS_ERR(rt))
+               return PTR_ERR(rt);
+
+       ip_rt_put(rt);
+
+       /* Generate egress_tun_info based on tun_info,
+        * saddr, tp_src and tp_dst
+        */
+       __ovs_flow_tun_info_init(egress_tun_info,
+                                saddr, tun_key->ipv4_dst,
+                                tun_key->ipv4_tos,
+                                tun_key->ipv4_ttl,
+                                tp_src, tp_dst,
+                                tun_key->tun_id,
+                                tun_key->tun_flags,
+                                tun_info->options,
+                                tun_info->options_len);
+
+       return 0;
+}
+
+int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
+                                 struct ovs_tunnel_info *info)
+{
+       /* get_egress_tun_info() is only implemented on tunnel ports. */
+       if (unlikely(!vport->ops->get_egress_tun_info))
+               return -EINVAL;
+
+       return vport->ops->get_egress_tun_info(vport, skb, info);
+}