vswitch: Add support for IPsec certificate authentication.
[openvswitch] / datapath / datapath.c
index 8960a060ef30dcedddaf45b93d001325fe2a3ce5..2be0ce12c6669dee6970c0e632a9edd6c93628f0 100644 (file)
@@ -94,6 +94,11 @@ static struct datapath *get_dp_locked(int dp_idx)
        return dp;
 }
 
+static struct tbl *get_table_protected(struct datapath *dp)
+{
+       return rcu_dereference_protected(dp->table, lockdep_is_held(&dp->mutex));
+}
+
 /* Must be called with rcu_read_lock or RTNL lock. */
 const char *dp_name(const struct datapath *dp)
 {
@@ -247,7 +252,7 @@ static int create_dp(int dp_idx, const char __user *devnamep)
 
        /* Allocate table. */
        err = -ENOMEM;
-       rcu_assign_pointer(dp->table, tbl_create(0));
+       rcu_assign_pointer(dp->table, tbl_create(TBL_MIN_BUCKETS));
        if (!dp->table)
                goto err_free_dp;
 
@@ -265,8 +270,10 @@ static int create_dp(int dp_idx, const char __user *devnamep)
 
        dp->drop_frags = 0;
        dp->stats_percpu = alloc_percpu(struct dp_stats_percpu);
-       if (!dp->stats_percpu)
+       if (!dp->stats_percpu) {
+               err = -ENOMEM;
                goto err_destroy_local_port;
+       }
 
        rcu_assign_pointer(dps[dp_idx], dp);
        dp_sysfs_add_dp(dp);
@@ -643,10 +650,10 @@ err:
 
 static int flush_flows(struct datapath *dp)
 {
-       struct tbl *old_table = rcu_dereference(dp->table);
+       struct tbl *old_table = get_table_protected(dp);
        struct tbl *new_table;
 
-       new_table = tbl_create(0);
+       new_table = tbl_create(TBL_MIN_BUCKETS);
        if (!new_table)
                return -ENOMEM;
 
@@ -741,7 +748,7 @@ static struct sw_flow_actions *get_actions(const struct odp_flow *flow)
 
        error = -EFAULT;
        if (copy_from_user(actions->actions,
-                          (struct nlattr __user *)flow->actions,
+                          (struct nlattr __user __force *)flow->actions,
                           flow->actions_len))
                goto error_free_actions;
        error = validate_actions(actions->actions, actions->actions_len);
@@ -790,7 +797,7 @@ static void clear_stats(struct sw_flow *flow)
 
 static int expand_table(struct datapath *dp)
 {
-       struct tbl *old_table = rcu_dereference(dp->table);
+       struct tbl *old_table = get_table_protected(dp);
        struct tbl *new_table;
 
        new_table = tbl_expand(old_table);
@@ -810,9 +817,11 @@ static int do_put_flow(struct datapath *dp, struct odp_flow_put *uf,
        struct sw_flow *flow;
        struct tbl *table;
        int error;
+       u32 hash;
 
-       table = rcu_dereference(dp->table);
-       flow_node = tbl_lookup(table, &uf->flow.key, flow_hash(&uf->flow.key), flow_cmp);
+       hash = flow_hash(&uf->flow.key);
+       table = get_table_protected(dp);
+       flow_node = tbl_lookup(table, &uf->flow.key, hash, flow_cmp);
        if (!flow_node) {
                /* No such flow. */
                struct sw_flow_actions *acts;
@@ -826,7 +835,7 @@ static int do_put_flow(struct datapath *dp, struct odp_flow_put *uf,
                        error = expand_table(dp);
                        if (error)
                                goto error;
-                       table = rcu_dereference(dp->table);
+                       table = get_table_protected(dp);
                }
 
                /* Allocate flow. */
@@ -846,7 +855,7 @@ static int do_put_flow(struct datapath *dp, struct odp_flow_put *uf,
                rcu_assign_pointer(flow->sf_acts, acts);
 
                /* Put flow in bucket. */
-               error = tbl_insert(table, &flow->tbl_node, flow_hash(&flow->key));
+               error = tbl_insert(table, &flow->tbl_node, hash);
                if (error)
                        goto error_free_flow_acts;
 
@@ -867,7 +876,9 @@ static int do_put_flow(struct datapath *dp, struct odp_flow_put *uf,
                error = PTR_ERR(new_acts);
                if (IS_ERR(new_acts))
                        goto error;
-               old_acts = rcu_dereference(flow->sf_acts);
+
+               old_acts = rcu_dereference_protected(flow->sf_acts,
+                                                    lockdep_is_held(&dp->mutex));
                if (old_acts->actions_len != new_acts->actions_len ||
                    memcmp(old_acts->actions, new_acts->actions,
                           old_acts->actions_len)) {
@@ -916,7 +927,8 @@ static int put_flow(struct datapath *dp, struct odp_flow_put __user *ufp)
        return 0;
 }
 
-static int do_answer_query(struct sw_flow *flow, u32 query_flags,
+static int do_answer_query(struct datapath *dp, struct sw_flow *flow,
+                          u32 query_flags,
                           struct odp_flow_stats __user *ustats,
                           struct nlattr __user *actions,
                           u32 __user *actions_lenp)
@@ -939,7 +951,8 @@ static int do_answer_query(struct sw_flow *flow, u32 query_flags,
        if (!actions_len)
                return 0;
 
-       sf_acts = rcu_dereference(flow->sf_acts);
+       sf_acts = rcu_dereference_protected(flow->sf_acts,
+                                           lockdep_is_held(&dp->mutex));
        if (put_user(sf_acts->actions_len, actions_lenp) ||
            (actions && copy_to_user(actions, sf_acts->actions,
                                     min(sf_acts->actions_len, actions_len))))
@@ -948,21 +961,21 @@ static int do_answer_query(struct sw_flow *flow, u32 query_flags,
        return 0;
 }
 
-static int answer_query(struct sw_flow *flow, u32 query_flags,
-                       struct odp_flow __user *ufp)
+static int answer_query(struct datapath *dp, struct sw_flow *flow,
+                       u32 query_flags, struct odp_flow __user *ufp)
 {
        struct nlattr __user *actions;
 
        if (get_user(actions, (struct nlattr __user * __user *)&ufp->actions))
                return -EFAULT;
 
-       return do_answer_query(flow, query_flags, 
+       return do_answer_query(dp, flow, query_flags, 
                               &ufp->stats, actions, &ufp->actions_len);
 }
 
 static struct sw_flow *do_del_flow(struct datapath *dp, struct odp_flow_key *key)
 {
-       struct tbl *table = rcu_dereference(dp->table);
+       struct tbl *table = get_table_protected(dp);
        struct tbl_node *flow_node;
        int error;
 
@@ -994,18 +1007,18 @@ static int del_flow(struct datapath *dp, struct odp_flow __user *ufp)
        if (IS_ERR(flow))
                return PTR_ERR(flow);
 
-       error = answer_query(flow, 0, ufp);
+       error = answer_query(dp, flow, 0, ufp);
        flow_deferred_free(flow);
        return error;
 }
 
 static int do_query_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
 {
-       struct tbl *table = rcu_dereference(dp->table);
+       struct tbl *table = get_table_protected(dp);
        u32 i;
 
        for (i = 0; i < flowvec->n_flows; i++) {
-               struct odp_flow __user *ufp = (struct odp_flow __user *)&flowvec->flows[i];
+               struct odp_flow __user *ufp = (struct odp_flow __user __force *)&flowvec->flows[i];
                struct odp_flow uf;
                struct tbl_node *flow_node;
                int error;
@@ -1017,7 +1030,7 @@ static int do_query_flows(struct datapath *dp, const struct odp_flowvec *flowvec
                if (!flow_node)
                        error = put_user(ENOENT, &ufp->stats.error);
                else
-                       error = answer_query(flow_cast(flow_node), uf.flags, ufp);
+                       error = answer_query(dp, flow_cast(flow_node), uf.flags, ufp);
                if (error)
                        return -EFAULT;
        }
@@ -1025,6 +1038,7 @@ static int do_query_flows(struct datapath *dp, const struct odp_flowvec *flowvec
 }
 
 struct list_flows_cbdata {
+       struct datapath *dp;
        struct odp_flow __user *uflows;
        u32 n_flows;
        u32 listed_flows;
@@ -1039,7 +1053,7 @@ static int list_flow(struct tbl_node *node, void *cbdata_)
 
        if (copy_to_user(&ufp->key, &flow->key, sizeof flow->key))
                return -EFAULT;
-       error = answer_query(flow, 0, ufp);
+       error = answer_query(cbdata->dp, flow, 0, ufp);
        if (error)
                return error;
 
@@ -1056,11 +1070,12 @@ static int do_list_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
        if (!flowvec->n_flows)
                return 0;
 
-       cbdata.uflows = (struct odp_flow __user *)flowvec->flows;
+       cbdata.dp = dp;
+       cbdata.uflows = (struct odp_flow __user __force*)flowvec->flows;
        cbdata.n_flows = flowvec->n_flows;
        cbdata.listed_flows = 0;
 
-       error = tbl_foreach(rcu_dereference(dp->table), list_flow, &cbdata);
+       error = tbl_foreach(get_table_protected(dp), list_flow, &cbdata);
        return error ? error : cbdata.listed_flows;
 }
 
@@ -1106,7 +1121,7 @@ static int do_execute(struct datapath *dp, const struct odp_execute *execute)
 
        err = -EFAULT;
        if (copy_from_user(actions->actions,
-           (struct nlattr __user *)execute->actions, execute->actions_len))
+           (struct nlattr __user __force *)execute->actions, execute->actions_len))
                goto error_free_actions;
 
        err = validate_actions(actions->actions, execute->actions_len);
@@ -1120,7 +1135,7 @@ static int do_execute(struct datapath *dp, const struct odp_execute *execute)
 
        err = -EFAULT;
        if (copy_from_user(skb_put(skb, execute->length),
-                          (const void __user *)execute->data,
+                          (const void __user __force *)execute->data,
                           execute->length))
                goto error_free_skb;
 
@@ -1166,7 +1181,7 @@ static int execute_packet(struct datapath *dp, const struct odp_execute __user *
 
 static int get_dp_stats(struct datapath *dp, struct odp_stats __user *statsp)
 {
-       struct tbl *table = rcu_dereference(dp->table);
+       struct tbl *table = get_table_protected(dp);
        struct odp_stats stats;
        int i;
 
@@ -1248,6 +1263,7 @@ static int put_port(const struct vport *p, struct odp_port __user *uop)
        rcu_read_lock();
        strncpy(op.devname, vport_get_name(p), sizeof op.devname);
        strncpy(op.type, vport_get_type(p), sizeof op.type);
+       vport_get_config(p, op.config);
        rcu_read_unlock();
 
        op.port = p->port_no;
@@ -1324,7 +1340,7 @@ static int list_ports(struct datapath *dp, struct odp_portvec __user *upv)
        if (copy_from_user(&pv, upv, sizeof pv))
                return -EFAULT;
 
-       retval = do_list_ports(dp, (struct odp_port __user *)pv.ports,
+       retval = do_list_ports(dp, (struct odp_port __user __force *)pv.ports,
                               pv.n_ports);
        if (retval < 0)
                return retval;
@@ -1554,7 +1570,8 @@ static int compat_put_flow(struct datapath *dp, struct compat_odp_flow_put __use
        return 0;
 }
 
-static int compat_answer_query(struct sw_flow *flow, u32 query_flags,
+static int compat_answer_query(struct datapath *dp, struct sw_flow *flow,
+                              u32 query_flags,
                               struct compat_odp_flow __user *ufp)
 {
        compat_uptr_t actions;
@@ -1562,7 +1579,7 @@ static int compat_answer_query(struct sw_flow *flow, u32 query_flags,
        if (get_user(actions, &ufp->actions))
                return -EFAULT;
 
-       return do_answer_query(flow, query_flags, &ufp->stats,
+       return do_answer_query(dp, flow, query_flags, &ufp->stats,
                               compat_ptr(actions), &ufp->actions_len);
 }
 
@@ -1579,7 +1596,7 @@ static int compat_del_flow(struct datapath *dp, struct compat_odp_flow __user *u
        if (IS_ERR(flow))
                return PTR_ERR(flow);
 
-       error = compat_answer_query(flow, 0, ufp);
+       error = compat_answer_query(dp, flow, 0, ufp);
        flow_deferred_free(flow);
        return error;
 }
@@ -1588,7 +1605,7 @@ static int compat_query_flows(struct datapath *dp,
                              struct compat_odp_flow __user *flows,
                              u32 n_flows)
 {
-       struct tbl *table = rcu_dereference(dp->table);
+       struct tbl *table = get_table_protected(dp);
        u32 i;
 
        for (i = 0; i < n_flows; i++) {
@@ -1604,7 +1621,8 @@ static int compat_query_flows(struct datapath *dp,
                if (!flow_node)
                        error = put_user(ENOENT, &ufp->stats.error);
                else
-                       error = compat_answer_query(flow_cast(flow_node), uf.flags, ufp);
+                       error = compat_answer_query(dp, flow_cast(flow_node),
+                                                   uf.flags, ufp);
                if (error)
                        return -EFAULT;
        }
@@ -1612,6 +1630,7 @@ static int compat_query_flows(struct datapath *dp,
 }
 
 struct compat_list_flows_cbdata {
+       struct datapath *dp;
        struct compat_odp_flow __user *uflows;
        u32 n_flows;
        u32 listed_flows;
@@ -1626,7 +1645,7 @@ static int compat_list_flow(struct tbl_node *node, void *cbdata_)
 
        if (copy_to_user(&ufp->key, &flow->key, sizeof flow->key))
                return -EFAULT;
-       error = compat_answer_query(flow, 0, ufp);
+       error = compat_answer_query(cbdata->dp, flow, 0, ufp);
        if (error)
                return error;
 
@@ -1644,11 +1663,12 @@ static int compat_list_flows(struct datapath *dp,
        if (!n_flows)
                return 0;
 
+       cbdata.dp = dp;
        cbdata.uflows = flows;
        cbdata.n_flows = n_flows;
        cbdata.listed_flows = 0;
 
-       error = tbl_foreach(rcu_dereference(dp->table), compat_list_flow, &cbdata);
+       error = tbl_foreach(get_table_protected(dp), compat_list_flow, &cbdata);
        return error ? error : cbdata.listed_flows;
 }
 
@@ -1921,6 +1941,8 @@ success:
                        u16 csum_start, csum_offset;
 
                        get_skb_csum_pointers(skb, &csum_start, &csum_offset);
+                       csum_start -= skb_headroom(skb);
+
                        BUG_ON(csum_start >= skb_headlen(skb));
                        retval = skb_copy_and_csum_datagram(skb, csum_start, buf + csum_start,
                                                            copy_bytes - csum_start, &csum);