Commit ef9a9d11 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

ipv6 sit: RCU conversion phase I

SIT tunnels use one rwlock to protect their prl entries.

This first patch adds RCU locking for prl management,
with standard call_rcu() calls.
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b37b62fe
...@@ -45,6 +45,7 @@ struct ip_tunnel_prl_entry ...@@ -45,6 +45,7 @@ struct ip_tunnel_prl_entry
struct ip_tunnel_prl_entry *next; struct ip_tunnel_prl_entry *next;
__be32 addr; __be32 addr;
u16 flags; u16 flags;
struct rcu_head rcu_head;
}; };
#define IPTUNNEL_XMIT() do { \ #define IPTUNNEL_XMIT() do { \
......
...@@ -240,15 +240,22 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct net *net, ...@@ -240,15 +240,22 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct net *net,
return NULL; return NULL;
} }
static DEFINE_SPINLOCK(ipip6_prl_lock);
#define for_each_prl_rcu(start) \
for (prl = rcu_dereference(start); \
prl; \
prl = rcu_dereference(prl->next))
static struct ip_tunnel_prl_entry * static struct ip_tunnel_prl_entry *
__ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr) __ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr)
{ {
struct ip_tunnel_prl_entry *p = (struct ip_tunnel_prl_entry *)NULL; struct ip_tunnel_prl_entry *prl;
for (p = t->prl; p; p = p->next) for_each_prl_rcu(t->prl)
if (p->addr == addr) if (prl->addr == addr)
break; break;
return p; return prl;
} }
...@@ -273,7 +280,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t, ...@@ -273,7 +280,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
kcalloc(cmax, sizeof(*kp), GFP_KERNEL) : kcalloc(cmax, sizeof(*kp), GFP_KERNEL) :
NULL; NULL;
read_lock(&ipip6_lock); rcu_read_lock();
ca = t->prl_count < cmax ? t->prl_count : cmax; ca = t->prl_count < cmax ? t->prl_count : cmax;
...@@ -291,7 +298,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t, ...@@ -291,7 +298,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
} }
c = 0; c = 0;
for (prl = t->prl; prl; prl = prl->next) { for_each_prl_rcu(t->prl) {
if (c >= cmax) if (c >= cmax)
break; break;
if (kprl.addr != htonl(INADDR_ANY) && prl->addr != kprl.addr) if (kprl.addr != htonl(INADDR_ANY) && prl->addr != kprl.addr)
...@@ -303,7 +310,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t, ...@@ -303,7 +310,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
break; break;
} }
out: out:
read_unlock(&ipip6_lock); rcu_read_unlock();
len = sizeof(*kp) * c; len = sizeof(*kp) * c;
ret = 0; ret = 0;
...@@ -324,12 +331,14 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg) ...@@ -324,12 +331,14 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
if (a->addr == htonl(INADDR_ANY)) if (a->addr == htonl(INADDR_ANY))
return -EINVAL; return -EINVAL;
write_lock(&ipip6_lock); spin_lock(&ipip6_prl_lock);
for (p = t->prl; p; p = p->next) { for (p = t->prl; p; p = p->next) {
if (p->addr == a->addr) { if (p->addr == a->addr) {
if (chg) if (chg) {
goto update; p->flags = a->flags;
goto out;
}
err = -EEXIST; err = -EEXIST;
goto out; goto out;
} }
...@@ -346,46 +355,63 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg) ...@@ -346,46 +355,63 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
goto out; goto out;
} }
INIT_RCU_HEAD(&p->rcu_head);
p->next = t->prl; p->next = t->prl;
t->prl = p;
t->prl_count++;
update:
p->addr = a->addr; p->addr = a->addr;
p->flags = a->flags; p->flags = a->flags;
t->prl_count++;
rcu_assign_pointer(t->prl, p);
out: out:
write_unlock(&ipip6_lock); spin_unlock(&ipip6_prl_lock);
return err; return err;
} }
static void prl_entry_destroy_rcu(struct rcu_head *head)
{
kfree(container_of(head, struct ip_tunnel_prl_entry, rcu_head));
}
static void prl_list_destroy_rcu(struct rcu_head *head)
{
struct ip_tunnel_prl_entry *p, *n;
p = container_of(head, struct ip_tunnel_prl_entry, rcu_head);
do {
n = p->next;
kfree(p);
p = n;
} while (p);
}
static int static int
ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
{ {
struct ip_tunnel_prl_entry *x, **p; struct ip_tunnel_prl_entry *x, **p;
int err = 0; int err = 0;
write_lock(&ipip6_lock); spin_lock(&ipip6_prl_lock);
if (a && a->addr != htonl(INADDR_ANY)) { if (a && a->addr != htonl(INADDR_ANY)) {
for (p = &t->prl; *p; p = &(*p)->next) { for (p = &t->prl; *p; p = &(*p)->next) {
if ((*p)->addr == a->addr) { if ((*p)->addr == a->addr) {
x = *p; x = *p;
*p = x->next; *p = x->next;
kfree(x); call_rcu(&x->rcu_head, prl_entry_destroy_rcu);
t->prl_count--; t->prl_count--;
goto out; goto out;
} }
} }
err = -ENXIO; err = -ENXIO;
} else { } else {
while (t->prl) { if (t->prl) {
t->prl_count = 0;
x = t->prl; x = t->prl;
t->prl = t->prl->next; call_rcu(&x->rcu_head, prl_list_destroy_rcu);
kfree(x); t->prl = NULL;
t->prl_count--;
} }
} }
out: out:
write_unlock(&ipip6_lock); spin_unlock(&ipip6_prl_lock);
return err; return err;
} }
...@@ -395,7 +421,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t) ...@@ -395,7 +421,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
struct ip_tunnel_prl_entry *p; struct ip_tunnel_prl_entry *p;
int ok = 1; int ok = 1;
read_lock(&ipip6_lock); rcu_read_lock();
p = __ipip6_tunnel_locate_prl(t, iph->saddr); p = __ipip6_tunnel_locate_prl(t, iph->saddr);
if (p) { if (p) {
if (p->flags & PRL_DEFAULT) if (p->flags & PRL_DEFAULT)
...@@ -411,7 +437,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t) ...@@ -411,7 +437,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
else else
ok = 0; ok = 0;
} }
read_unlock(&ipip6_lock); rcu_read_unlock();
return ok; return ok;
} }
...@@ -1192,6 +1218,7 @@ static void __exit sit_cleanup(void) ...@@ -1192,6 +1218,7 @@ static void __exit sit_cleanup(void)
xfrm4_tunnel_deregister(&sit_handler, AF_INET6); xfrm4_tunnel_deregister(&sit_handler, AF_INET6);
unregister_pernet_gen_device(sit_net_id, &sit_net_ops); unregister_pernet_gen_device(sit_net_id, &sit_net_ops);
rcu_barrier(); /* Wait for completion of call_rcu()'s */
} }
static int __init sit_init(void) static int __init sit_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment