Skip to content

Commit

Permalink
ipv6 sit: RCU conversion phase I
Browse files Browse the repository at this point in the history
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: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Eric Dumazet authored and davem330 committed Oct 24, 2009
1 parent b37b62f commit ef9a9d1
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 23 deletions.
1 change: 1 addition & 0 deletions include/net/ipip.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct ip_tunnel_prl_entry
struct ip_tunnel_prl_entry *next;
__be32 addr;
u16 flags;
struct rcu_head rcu_head;
};

#define IPTUNNEL_XMIT() do { \
Expand Down
73 changes: 50 additions & 23 deletions net/ipv6/sit.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,15 +240,22 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct net *net,
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 *
__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)
if (p->addr == addr)
for_each_prl_rcu(t->prl)
if (prl->addr == addr)
break;
return p;
return prl;

}

Expand All @@ -273,7 +280,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
kcalloc(cmax, sizeof(*kp), GFP_KERNEL) :
NULL;

read_lock(&ipip6_lock);
rcu_read_lock();

ca = t->prl_count < cmax ? t->prl_count : cmax;

Expand All @@ -291,7 +298,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
}

c = 0;
for (prl = t->prl; prl; prl = prl->next) {
for_each_prl_rcu(t->prl) {
if (c >= cmax)
break;
if (kprl.addr != htonl(INADDR_ANY) && prl->addr != kprl.addr)
Expand All @@ -303,7 +310,7 @@ static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
break;
}
out:
read_unlock(&ipip6_lock);
rcu_read_unlock();

len = sizeof(*kp) * c;
ret = 0;
Expand All @@ -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))
return -EINVAL;

write_lock(&ipip6_lock);
spin_lock(&ipip6_prl_lock);

for (p = t->prl; p; p = p->next) {
if (p->addr == a->addr) {
if (chg)
goto update;
if (chg) {
p->flags = a->flags;
goto out;
}
err = -EEXIST;
goto out;
}
Expand All @@ -346,46 +355,63 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
goto out;
}

INIT_RCU_HEAD(&p->rcu_head);
p->next = t->prl;
t->prl = p;
t->prl_count++;
update:
p->addr = a->addr;
p->flags = a->flags;
t->prl_count++;
rcu_assign_pointer(t->prl, p);
out:
write_unlock(&ipip6_lock);
spin_unlock(&ipip6_prl_lock);
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
ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
{
struct ip_tunnel_prl_entry *x, **p;
int err = 0;

write_lock(&ipip6_lock);
spin_lock(&ipip6_prl_lock);

if (a && a->addr != htonl(INADDR_ANY)) {
for (p = &t->prl; *p; p = &(*p)->next) {
if ((*p)->addr == a->addr) {
x = *p;
*p = x->next;
kfree(x);
call_rcu(&x->rcu_head, prl_entry_destroy_rcu);
t->prl_count--;
goto out;
}
}
err = -ENXIO;
} else {
while (t->prl) {
if (t->prl) {
t->prl_count = 0;
x = t->prl;
t->prl = t->prl->next;
kfree(x);
t->prl_count--;
call_rcu(&x->rcu_head, prl_list_destroy_rcu);
t->prl = NULL;
}
}
out:
write_unlock(&ipip6_lock);
spin_unlock(&ipip6_prl_lock);
return err;
}

Expand All @@ -395,7 +421,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
struct ip_tunnel_prl_entry *p;
int ok = 1;

read_lock(&ipip6_lock);
rcu_read_lock();
p = __ipip6_tunnel_locate_prl(t, iph->saddr);
if (p) {
if (p->flags & PRL_DEFAULT)
Expand All @@ -411,7 +437,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
else
ok = 0;
}
read_unlock(&ipip6_lock);
rcu_read_unlock();
return ok;
}

Expand Down Expand Up @@ -1192,6 +1218,7 @@ static void __exit sit_cleanup(void)
xfrm4_tunnel_deregister(&sit_handler, AF_INET6);

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)
Expand Down

0 comments on commit ef9a9d1

Please sign in to comment.