datapath: Use call_rcu() when deleting a datapath.
[openvswitch] / datapath / tunnel.c
index 5a9f8f360a3f8d0a6fdf2965065ace94af809569..e42e0e361fbf562a36ac0149dfc037cd8dccfe7e 100644 (file)
@@ -1372,6 +1372,7 @@ struct vport *tnl_create(const struct vport_parms *parms,
 {
        struct vport *vport;
        struct tnl_vport *tnl_vport;
+       struct tnl_mutable_config *mutable;
        int initial_frag_id;
        int err;
 
@@ -1386,19 +1387,19 @@ struct vport *tnl_create(const struct vport_parms *parms,
        strcpy(tnl_vport->name, parms->name);
        tnl_vport->tnl_ops = tnl_ops;
 
-       tnl_vport->mutable = kzalloc(sizeof(struct tnl_mutable_config), GFP_KERNEL);
-       if (!tnl_vport->mutable) {
+       mutable = kzalloc(sizeof(struct tnl_mutable_config), GFP_KERNEL);
+       if (!mutable) {
                err = -ENOMEM;
                goto error_free_vport;
        }
 
-       vport_gen_rand_ether_addr(tnl_vport->mutable->eth_addr);
-       tnl_vport->mutable->mtu = ETH_DATA_LEN;
+       vport_gen_rand_ether_addr(mutable->eth_addr);
+       mutable->mtu = ETH_DATA_LEN;
 
        get_random_bytes(&initial_frag_id, sizeof(int));
        atomic_set(&tnl_vport->frag_id, initial_frag_id);
 
-       err = set_config(parms->config, tnl_ops, NULL, tnl_vport->mutable);
+       err = set_config(parms->config, tnl_ops, NULL, mutable);
        if (err)
                goto error_free_mutable;
 
@@ -1406,9 +1407,11 @@ struct vport *tnl_create(const struct vport_parms *parms,
 
 #ifdef NEED_CACHE_TIMEOUT
        tnl_vport->cache_exp_interval = MAX_CACHE_EXP -
-                                       (net_random() % (MAX_CACHE_EXP / 2));
+                                      (net_random() % (MAX_CACHE_EXP / 2));
 #endif
 
+       rcu_assign_pointer(tnl_vport->mutable, mutable);
+
        err = add_port(vport);
        if (err)
                goto error_free_mutable;
@@ -1416,7 +1419,7 @@ struct vport *tnl_create(const struct vport_parms *parms,
        return vport;
 
 error_free_mutable:
-       kfree(tnl_vport->mutable);
+       kfree(mutable);
 error_free_vport:
        vport_free(vport);
 error:
@@ -1429,7 +1432,8 @@ int tnl_modify(struct vport *vport, struct odp_port *port)
        struct tnl_mutable_config *mutable;
        int err;
 
-       mutable = kmemdup(tnl_vport->mutable, sizeof(struct tnl_mutable_config), GFP_KERNEL);
+       mutable = kmemdup(rtnl_dereference(tnl_vport->mutable),
+                         sizeof(struct tnl_mutable_config), GFP_KERNEL);
        if (!mutable) {
                err = -ENOMEM;
                goto error;
@@ -1455,26 +1459,24 @@ error:
 
 static void free_port_rcu(struct rcu_head *rcu)
 {
-       struct tnl_vport *tnl_vport = container_of(rcu, struct tnl_vport, rcu);
-
-       spin_lock_bh(&tnl_vport->cache_lock);
-       free_cache(tnl_vport->cache);
-       spin_unlock_bh(&tnl_vport->cache_lock);
+       struct tnl_vport *tnl_vport = container_of(rcu,
+                                                  struct tnl_vport, rcu);
 
-       kfree(tnl_vport->mutable);
+       free_cache((struct tnl_cache __force *)tnl_vport->cache);
+       kfree((struct tnl_mutable __force *)tnl_vport->mutable);
        vport_free(tnl_vport_to_vport(tnl_vport));
 }
 
 int tnl_destroy(struct vport *vport)
 {
        struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       const struct tnl_mutable_config *old_mutable;
+       const struct tnl_mutable_config *mutable, *old_mutable;
+
+       mutable = rtnl_dereference(tnl_vport->mutable);
 
-       if (vport == tnl_find_port(tnl_vport->mutable->port_config.saddr,
-           tnl_vport->mutable->port_config.daddr,
-           tnl_vport->mutable->port_config.in_key,
-           tnl_vport->mutable->tunnel_type,
-           &old_mutable))
+       if (vport == tnl_find_port(mutable->port_config.saddr,
+           mutable->port_config.daddr, mutable->port_config.in_key,
+           mutable->tunnel_type, &old_mutable))
                del_port(vport);
 
        call_rcu(&tnl_vport->rcu, free_port_rcu);
@@ -1487,7 +1489,8 @@ int tnl_set_mtu(struct vport *vport, int mtu)
        struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
        struct tnl_mutable_config *mutable;
 
-       mutable = kmemdup(tnl_vport->mutable, sizeof(struct tnl_mutable_config), GFP_KERNEL);
+       mutable = kmemdup(rtnl_dereference(tnl_vport->mutable),
+                         sizeof(struct tnl_mutable_config), GFP_KERNEL);
        if (!mutable)
                return -ENOMEM;
 
@@ -1502,7 +1505,8 @@ int tnl_set_addr(struct vport *vport, const unsigned char *addr)
        struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
        struct tnl_mutable_config *mutable;
 
-       mutable = kmemdup(tnl_vport->mutable, sizeof(struct tnl_mutable_config), GFP_KERNEL);
+       mutable = kmemdup(rtnl_dereference(tnl_vport->mutable),
+                         sizeof(struct tnl_mutable_config), GFP_KERNEL);
        if (!mutable)
                return -ENOMEM;
 
@@ -1524,6 +1528,15 @@ const unsigned char *tnl_get_addr(const struct vport *vport)
        return rcu_dereference_rtnl(tnl_vport->mutable)->eth_addr;
 }
 
+void tnl_get_config(const struct vport *vport, void *config)
+{
+       const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+       struct tnl_port_config *port_config;
+       
+       port_config = &rcu_dereference_rtnl(tnl_vport->mutable)->port_config;
+       memcpy(config, port_config, sizeof(*port_config));
+}
+
 int tnl_get_mtu(const struct vport *vport)
 {
        const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);