Commit 12a169e7 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

ipsec: Put dumpers on the dump list

Herbert Xu came up with the idea and the original patch to make
xfrm_state dump list contain also dumpers:

As it is we go to extraordinary lengths to ensure that states
don't go away while dumpers go to sleep.  It's much easier if
we just put the dumpers themselves on the list since they can't
go away while they're going.

I've also changed the order of addition on new states to prevent
a never-ending dump.

Timo Teräs improved the patch to apply cleanly to latest tree,
modified iteration code to be more readable by using a common
struct for entries in the list, implemented the same idea for
xfrm_policy dumping and moved the af_key specific "last" entry
caching to af_key.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarTimo Teras <timo.teras@iki.fi>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b262e603
...@@ -220,7 +220,7 @@ struct netlink_callback ...@@ -220,7 +220,7 @@ struct netlink_callback
int (*dump)(struct sk_buff * skb, struct netlink_callback *cb); int (*dump)(struct sk_buff * skb, struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb); int (*done)(struct netlink_callback *cb);
int family; int family;
long args[7]; long args[6];
}; };
struct netlink_notify struct netlink_notify
......
...@@ -117,12 +117,21 @@ extern struct mutex xfrm_cfg_mutex; ...@@ -117,12 +117,21 @@ extern struct mutex xfrm_cfg_mutex;
metrics. Plus, it will be made via sk->sk_dst_cache. Solved. metrics. Plus, it will be made via sk->sk_dst_cache. Solved.
*/ */
struct xfrm_state_walk {
struct list_head all;
u8 state;
union {
u8 dying;
u8 proto;
};
u32 seq;
};
/* Full description of state of transformer. */ /* Full description of state of transformer. */
struct xfrm_state struct xfrm_state
{ {
struct list_head all;
union { union {
struct list_head gclist; struct hlist_node gclist;
struct hlist_node bydst; struct hlist_node bydst;
}; };
struct hlist_node bysrc; struct hlist_node bysrc;
...@@ -136,12 +145,8 @@ struct xfrm_state ...@@ -136,12 +145,8 @@ struct xfrm_state
u32 genid; u32 genid;
/* Key manger bits */ /* Key manager bits */
struct { struct xfrm_state_walk km;
u8 state;
u8 dying;
u32 seq;
} km;
/* Parameters of this state. */ /* Parameters of this state. */
struct { struct {
...@@ -449,10 +454,20 @@ struct xfrm_tmpl ...@@ -449,10 +454,20 @@ struct xfrm_tmpl
#define XFRM_MAX_DEPTH 6 #define XFRM_MAX_DEPTH 6
struct xfrm_policy_walk_entry {
struct list_head all;
u8 dead;
};
struct xfrm_policy_walk {
struct xfrm_policy_walk_entry walk;
u8 type;
u32 seq;
};
struct xfrm_policy struct xfrm_policy
{ {
struct xfrm_policy *next; struct xfrm_policy *next;
struct list_head bytype;
struct hlist_node bydst; struct hlist_node bydst;
struct hlist_node byidx; struct hlist_node byidx;
...@@ -467,13 +482,12 @@ struct xfrm_policy ...@@ -467,13 +482,12 @@ struct xfrm_policy
struct xfrm_lifetime_cfg lft; struct xfrm_lifetime_cfg lft;
struct xfrm_lifetime_cur curlft; struct xfrm_lifetime_cur curlft;
struct dst_entry *bundles; struct dst_entry *bundles;
u16 family; struct xfrm_policy_walk_entry walk;
u8 type; u8 type;
u8 action; u8 action;
u8 flags; u8 flags;
u8 dead;
u8 xfrm_nr; u8 xfrm_nr;
/* XXX 1 byte hole, try to pack */ u16 family;
struct xfrm_sec_ctx *security; struct xfrm_sec_ctx *security;
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH]; struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
}; };
...@@ -1245,20 +1259,6 @@ struct xfrm6_tunnel { ...@@ -1245,20 +1259,6 @@ struct xfrm6_tunnel {
int priority; int priority;
}; };
struct xfrm_state_walk {
struct list_head list;
unsigned long genid;
struct xfrm_state *state;
int count;
u8 proto;
};
struct xfrm_policy_walk {
struct xfrm_policy *policy;
int count;
u8 type, cur_type;
};
extern void xfrm_init(void); extern void xfrm_init(void);
extern void xfrm4_init(void); extern void xfrm4_init(void);
extern void xfrm_state_init(void); extern void xfrm_state_init(void);
...@@ -1410,24 +1410,10 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -1410,24 +1410,10 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp); struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp);
static inline void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type) extern void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type);
{
walk->cur_type = XFRM_POLICY_TYPE_MAIN;
walk->type = type;
walk->policy = NULL;
walk->count = 0;
}
static inline void xfrm_policy_walk_done(struct xfrm_policy_walk *walk)
{
if (walk->policy != NULL) {
xfrm_pol_put(walk->policy);
walk->policy = NULL;
}
}
extern int xfrm_policy_walk(struct xfrm_policy_walk *walk, extern int xfrm_policy_walk(struct xfrm_policy_walk *walk,
int (*func)(struct xfrm_policy *, int, int, void*), void *); int (*func)(struct xfrm_policy *, int, int, void*), void *);
extern void xfrm_policy_walk_done(struct xfrm_policy_walk *walk);
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl); int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir, struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
struct xfrm_selector *sel, struct xfrm_selector *sel,
......
...@@ -58,6 +58,7 @@ struct pfkey_sock { ...@@ -58,6 +58,7 @@ struct pfkey_sock {
struct xfrm_policy_walk policy; struct xfrm_policy_walk policy;
struct xfrm_state_walk state; struct xfrm_state_walk state;
} u; } u;
struct sk_buff *skb;
} dump; } dump;
}; };
...@@ -76,6 +77,10 @@ static int pfkey_can_dump(struct sock *sk) ...@@ -76,6 +77,10 @@ static int pfkey_can_dump(struct sock *sk)
static void pfkey_terminate_dump(struct pfkey_sock *pfk) static void pfkey_terminate_dump(struct pfkey_sock *pfk)
{ {
if (pfk->dump.dump) { if (pfk->dump.dump) {
if (pfk->dump.skb) {
kfree_skb(pfk->dump.skb);
pfk->dump.skb = NULL;
}
pfk->dump.done(pfk); pfk->dump.done(pfk);
pfk->dump.dump = NULL; pfk->dump.dump = NULL;
pfk->dump.done = NULL; pfk->dump.done = NULL;
...@@ -308,12 +313,25 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, ...@@ -308,12 +313,25 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
static int pfkey_do_dump(struct pfkey_sock *pfk) static int pfkey_do_dump(struct pfkey_sock *pfk)
{ {
struct sadb_msg *hdr;
int rc; int rc;
rc = pfk->dump.dump(pfk); rc = pfk->dump.dump(pfk);
if (rc == -ENOBUFS) if (rc == -ENOBUFS)
return 0; return 0;
if (pfk->dump.skb) {
if (!pfkey_can_dump(&pfk->sk))
return 0;
hdr = (struct sadb_msg *) pfk->dump.skb->data;
hdr->sadb_msg_seq = 0;
hdr->sadb_msg_errno = rc;
pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
&pfk->sk);
pfk->dump.skb = NULL;
}
pfkey_terminate_dump(pfk); pfkey_terminate_dump(pfk);
return rc; return rc;
} }
...@@ -1744,9 +1762,14 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr) ...@@ -1744,9 +1762,14 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr)
out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto); out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_errno = 0;
out_hdr->sadb_msg_reserved = 0; out_hdr->sadb_msg_reserved = 0;
out_hdr->sadb_msg_seq = count; out_hdr->sadb_msg_seq = count + 1;
out_hdr->sadb_msg_pid = pfk->dump.msg_pid; out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, &pfk->sk);
if (pfk->dump.skb)
pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
&pfk->sk);
pfk->dump.skb = out_skb;
return 0; return 0;
} }
...@@ -2245,7 +2268,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h ...@@ -2245,7 +2268,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
return 0; return 0;
out: out:
xp->dead = 1; xp->walk.dead = 1;
xfrm_policy_destroy(xp); xfrm_policy_destroy(xp);
return err; return err;
} }
...@@ -2583,9 +2606,14 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr) ...@@ -2583,9 +2606,14 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
out_hdr->sadb_msg_type = SADB_X_SPDDUMP; out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC; out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_errno = 0;
out_hdr->sadb_msg_seq = count; out_hdr->sadb_msg_seq = count + 1;
out_hdr->sadb_msg_pid = pfk->dump.msg_pid; out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, &pfk->sk);
if (pfk->dump.skb)
pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
&pfk->sk);
pfk->dump.skb = out_skb;
return 0; return 0;
} }
......
...@@ -46,7 +46,7 @@ EXPORT_SYMBOL(xfrm_cfg_mutex); ...@@ -46,7 +46,7 @@ EXPORT_SYMBOL(xfrm_cfg_mutex);
static DEFINE_RWLOCK(xfrm_policy_lock); static DEFINE_RWLOCK(xfrm_policy_lock);
static struct list_head xfrm_policy_bytype[XFRM_POLICY_TYPE_MAX]; static struct list_head xfrm_policy_all;
unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2]; unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];
EXPORT_SYMBOL(xfrm_policy_count); EXPORT_SYMBOL(xfrm_policy_count);
...@@ -164,7 +164,7 @@ static void xfrm_policy_timer(unsigned long data) ...@@ -164,7 +164,7 @@ static void xfrm_policy_timer(unsigned long data)
read_lock(&xp->lock); read_lock(&xp->lock);
if (xp->dead) if (xp->walk.dead)
goto out; goto out;
dir = xfrm_policy_id2dir(xp->index); dir = xfrm_policy_id2dir(xp->index);
...@@ -236,7 +236,7 @@ struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp) ...@@ -236,7 +236,7 @@ struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
policy = kzalloc(sizeof(struct xfrm_policy), gfp); policy = kzalloc(sizeof(struct xfrm_policy), gfp);
if (policy) { if (policy) {
INIT_LIST_HEAD(&policy->bytype); INIT_LIST_HEAD(&policy->walk.all);
INIT_HLIST_NODE(&policy->bydst); INIT_HLIST_NODE(&policy->bydst);
INIT_HLIST_NODE(&policy->byidx); INIT_HLIST_NODE(&policy->byidx);
rwlock_init(&policy->lock); rwlock_init(&policy->lock);
...@@ -252,17 +252,13 @@ EXPORT_SYMBOL(xfrm_policy_alloc); ...@@ -252,17 +252,13 @@ EXPORT_SYMBOL(xfrm_policy_alloc);
void xfrm_policy_destroy(struct xfrm_policy *policy) void xfrm_policy_destroy(struct xfrm_policy *policy)
{ {
BUG_ON(!policy->dead); BUG_ON(!policy->walk.dead);
BUG_ON(policy->bundles); BUG_ON(policy->bundles);
if (del_timer(&policy->timer)) if (del_timer(&policy->timer))
BUG(); BUG();
write_lock_bh(&xfrm_policy_lock);
list_del(&policy->bytype);
write_unlock_bh(&xfrm_policy_lock);
security_xfrm_policy_free(policy->security); security_xfrm_policy_free(policy->security);
kfree(policy); kfree(policy);
} }
...@@ -310,8 +306,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy) ...@@ -310,8 +306,8 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
int dead; int dead;
write_lock_bh(&policy->lock); write_lock_bh(&policy->lock);
dead = policy->dead; dead = policy->walk.dead;
policy->dead = 1; policy->walk.dead = 1;
write_unlock_bh(&policy->lock); write_unlock_bh(&policy->lock);
if (unlikely(dead)) { if (unlikely(dead)) {
...@@ -609,6 +605,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) ...@@ -609,6 +605,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
if (delpol) { if (delpol) {
hlist_del(&delpol->bydst); hlist_del(&delpol->bydst);
hlist_del(&delpol->byidx); hlist_del(&delpol->byidx);
list_del(&delpol->walk.all);
xfrm_policy_count[dir]--; xfrm_policy_count[dir]--;
} }
policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir); policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);
...@@ -617,7 +614,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) ...@@ -617,7 +614,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
policy->curlft.use_time = 0; policy->curlft.use_time = 0;
if (!mod_timer(&policy->timer, jiffies + HZ)) if (!mod_timer(&policy->timer, jiffies + HZ))
xfrm_pol_hold(policy); xfrm_pol_hold(policy);
list_add_tail(&policy->bytype, &xfrm_policy_bytype[policy->type]); list_add(&policy->walk.all, &xfrm_policy_all);
write_unlock_bh(&xfrm_policy_lock); write_unlock_bh(&xfrm_policy_lock);
if (delpol) if (delpol)
...@@ -684,6 +681,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir, ...@@ -684,6 +681,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
} }
hlist_del(&pol->bydst); hlist_del(&pol->bydst);
hlist_del(&pol->byidx); hlist_del(&pol->byidx);
list_del(&pol->walk.all);
xfrm_policy_count[dir]--; xfrm_policy_count[dir]--;
} }
ret = pol; ret = pol;
...@@ -727,6 +725,7 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete, ...@@ -727,6 +725,7 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete,
} }
hlist_del(&pol->bydst); hlist_del(&pol->bydst);
hlist_del(&pol->byidx); hlist_del(&pol->byidx);
list_del(&pol->walk.all);
xfrm_policy_count[dir]--; xfrm_policy_count[dir]--;
} }
ret = pol; ret = pol;
...@@ -840,6 +839,7 @@ int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info) ...@@ -840,6 +839,7 @@ int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
continue; continue;
hlist_del(&pol->bydst); hlist_del(&pol->bydst);
hlist_del(&pol->byidx); hlist_del(&pol->byidx);
list_del(&pol->walk.all);
write_unlock_bh(&xfrm_policy_lock); write_unlock_bh(&xfrm_policy_lock);
xfrm_audit_policy_delete(pol, 1, xfrm_audit_policy_delete(pol, 1,
...@@ -867,60 +867,68 @@ int xfrm_policy_walk(struct xfrm_policy_walk *walk, ...@@ -867,60 +867,68 @@ int xfrm_policy_walk(struct xfrm_policy_walk *walk,
int (*func)(struct xfrm_policy *, int, int, void*), int (*func)(struct xfrm_policy *, int, int, void*),
void *data) void *data)
{ {
struct xfrm_policy *old, *pol, *last = NULL; struct xfrm_policy *pol;
struct xfrm_policy_walk_entry *x;
int error = 0; int error = 0;
if (walk->type >= XFRM_POLICY_TYPE_MAX && if (walk->type >= XFRM_POLICY_TYPE_MAX &&
walk->type != XFRM_POLICY_TYPE_ANY) walk->type != XFRM_POLICY_TYPE_ANY)
return -EINVAL; return -EINVAL;
if (walk->policy == NULL && walk->count != 0) if (list_empty(&walk->walk.all) && walk->seq != 0)
return 0; return 0;
old = pol = walk->policy; write_lock_bh(&xfrm_policy_lock);
walk->policy = NULL; if (list_empty(&walk->walk.all))
read_lock_bh(&xfrm_policy_lock); x = list_first_entry(&xfrm_policy_all, struct xfrm_policy_walk_entry, all);
else
for (; walk->cur_type < XFRM_POLICY_TYPE_MAX; walk->cur_type++) { x = list_entry(&walk->walk.all, struct xfrm_policy_walk_entry, all);
if (walk->type != walk->cur_type && list_for_each_entry_from(x, &xfrm_policy_all, all) {
walk->type != XFRM_POLICY_TYPE_ANY) if (x->dead)
continue; continue;
pol = container_of(x, struct xfrm_policy, walk);
if (pol == NULL) { if (walk->type != XFRM_POLICY_TYPE_ANY &&
pol = list_first_entry(&xfrm_policy_bytype[walk->cur_type], walk->type != pol->type)
struct xfrm_policy, bytype); continue;
} error = func(pol, xfrm_policy_id2dir(pol->index),
list_for_each_entry_from(pol, &xfrm_policy_bytype[walk->cur_type], bytype) { walk->seq, data);
if (pol->dead) if (error) {
continue; list_move_tail(&walk->walk.all, &x->all);
if (last) { goto out;
error = func(last, xfrm_policy_id2dir(last->index),
walk->count, data);
if (error) {
xfrm_pol_hold(last);
walk->policy = last;
goto out;
}
}
last = pol;
walk->count++;
} }
pol = NULL; walk->seq++;
} }
if (walk->count == 0) { if (walk->seq == 0) {
error = -ENOENT; error = -ENOENT;
goto out; goto out;
} }
if (last) list_del_init(&walk->walk.all);
error = func(last, xfrm_policy_id2dir(last->index), 0, data);
out: out:
read_unlock_bh(&xfrm_policy_lock); write_unlock_bh(&xfrm_policy_lock);
if (old != NULL)
xfrm_pol_put(old);
return error; return error;
} }
EXPORT_SYMBOL(xfrm_policy_walk); EXPORT_SYMBOL(xfrm_policy_walk);
void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type)
{
INIT_LIST_HEAD(&walk->walk.all);
walk->walk.dead = 1;
walk->type = type;
walk->seq = 0;
}
EXPORT_SYMBOL(xfrm_policy_walk_init);
void xfrm_policy_walk_done(struct xfrm_policy_walk *walk)
{
if (list_empty(&walk->walk.all))
return;
write_lock_bh(&xfrm_policy_lock);
list_del(&walk->walk.all);
write_unlock_bh(&xfrm_policy_lock);
}
EXPORT_SYMBOL(xfrm_policy_walk_done);
/* /*
* Find policy to apply to this flow. * Find policy to apply to this flow.
* *
...@@ -1077,7 +1085,7 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir) ...@@ -1077,7 +1085,7 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir)
struct hlist_head *chain = policy_hash_bysel(&pol->selector, struct hlist_head *chain = policy_hash_bysel(&pol->selector,
pol->family, dir); pol->family, dir);
list_add_tail(&pol->bytype, &xfrm_policy_bytype[pol->type]); list_add(&pol->walk.all, &xfrm_policy_all);
hlist_add_head(&pol->bydst, chain); hlist_add_head(&pol->bydst, chain);
hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index)); hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index));
xfrm_policy_count[dir]++; xfrm_policy_count[dir]++;
...@@ -1095,6 +1103,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, ...@@ -1095,6 +1103,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
hlist_del(&pol->bydst); hlist_del(&pol->bydst);
hlist_del(&pol->byidx); hlist_del(&pol->byidx);
list_del(&pol->walk.all);
xfrm_policy_count[dir]--; xfrm_policy_count[dir]--;
return pol; return pol;
...@@ -1720,7 +1729,7 @@ int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, ...@@ -1720,7 +1729,7 @@ int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
for (pi = 0; pi < npols; pi++) { for (pi = 0; pi < npols; pi++) {
read_lock_bh(&pols[pi]->lock); read_lock_bh(&pols[pi]->lock);
pol_dead |= pols[pi]->dead; pol_dead |= pols[pi]->walk.dead;
read_unlock_bh(&pols[pi]->lock); read_unlock_bh(&pols[pi]->lock);
} }
...@@ -2415,9 +2424,7 @@ static void __init xfrm_policy_init(void) ...@@ -2415,9 +2424,7 @@ static void __init xfrm_policy_init(void)
panic("XFRM: failed to allocate bydst hash\n"); panic("XFRM: failed to allocate bydst hash\n");
} }
for (dir = 0; dir < XFRM_POLICY_TYPE_MAX; dir++) INIT_LIST_HEAD(&xfrm_policy_all);
INIT_LIST_HEAD(&xfrm_policy_bytype[dir]);
INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task); INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task);
register_netdevice_notifier(&xfrm_dev_notifier); register_netdevice_notifier(&xfrm_dev_notifier);
} }
...@@ -2601,7 +2608,7 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol, ...@@ -2601,7 +2608,7 @@ static int xfrm_policy_migrate(struct xfrm_policy *pol,
int i, j, n = 0; int i, j, n = 0;
write_lock_bh(&pol->lock); write_lock_bh(&pol->lock);
if (unlikely(pol->dead)) { if (unlikely(pol->walk.dead)) {
/* target policy has been deleted */ /* target policy has been deleted */
write_unlock_bh(&pol->lock); write_unlock_bh(&pol->lock);
return -ENOENT; return -ENOENT;
......
...@@ -59,14 +59,6 @@ static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; ...@@ -59,14 +59,6 @@ static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
static unsigned int xfrm_state_num; static unsigned int xfrm_state_num;
static unsigned int xfrm_state_genid; static unsigned int xfrm_state_genid;
/* Counter indicating ongoing walk, protected by xfrm_state_lock. */
static unsigned long xfrm_state_walk_ongoing;
/* Counter indicating walk completion, protected by xfrm_cfg_mutex. */
static unsigned long xfrm_state_walk_completed;
/* List of outstanding state walks used to set the completed counter. */
static LIST_HEAD(xfrm_state_walks);
static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
...@@ -199,8 +191,7 @@ static DEFINE_RWLOCK(xfrm_state_afinfo_lock); ...@@ -199,8 +191,7 @@ static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
static struct work_struct xfrm_state_gc_work; static struct work_struct xfrm_state_gc_work;
static LIST_HEAD(xfrm_state_gc_leftovers); static HLIST_HEAD(xfrm_state_gc_list);
static LIST_HEAD(xfrm_state_gc_list);
static DEFINE_SPINLOCK(xfrm_state_gc_lock); static DEFINE_SPINLOCK(xfrm_state_gc_lock);
int __xfrm_state_delete(struct xfrm_state *x); int __xfrm_state_delete(struct xfrm_state *x);
...@@ -412,23 +403,16 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x) ...@@ -412,23 +403,16 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
static void xfrm_state_gc_task(struct work_struct *data) static void xfrm_state_gc_task(struct work_struct *data)
{ {
struct xfrm_state *x, *tmp; struct xfrm_state *x;
unsigned long completed; struct hlist_node *entry, *tmp;
struct hlist_head gc_list;
mutex_lock(&xfrm_cfg_mutex);
spin_lock_bh(&xfrm_state_gc_lock); spin_lock_bh(&xfrm_state_gc_lock);
list_splice_tail_init(&xfrm_state_gc_list, &xfrm_state_gc_leftovers); hlist_move_list(&xfrm_state_gc_list, &gc_list);
spin_unlock_bh(&xfrm_state_gc_lock); spin_unlock_bh(&xfrm_state_gc_lock);
completed = xfrm_state_walk_completed; hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
mutex_unlock(&xfrm_cfg_mutex);
list_for_each_entry_safe(x, tmp, &xfrm_state_gc_leftovers, gclist) {
if ((long)(x->lastused - completed) > 0)
break;
list_del(&x->gclist);
xfrm_state_gc_destroy(x); xfrm_state_gc_destroy(x);
}
wake_up(&km_waitq); wake_up(&km_waitq);
} }
...@@ -529,7 +513,7 @@ struct xfrm_state *xfrm_state_alloc(void) ...@@ -529,7 +513,7 @@ struct xfrm_state *xfrm_state_alloc(void)
if (x) { if (x) {
atomic_set(&x->refcnt, 1); atomic_set(&x->refcnt, 1);
atomic_set(&x->tunnel_users, 0); atomic_set(&x->tunnel_users, 0);
INIT_LIST_HEAD(&x->all); INIT_LIST_HEAD(&x->km.all);
INIT_HLIST_NODE(&x->bydst); INIT_HLIST_NODE(&x->bydst);
INIT_HLIST_NODE(&x->bysrc); INIT_HLIST_NODE(&x->bysrc);
INIT_HLIST_NODE(&x->byspi); INIT_HLIST_NODE(&x->byspi);
...@@ -556,7 +540,7 @@ void __xfrm_state_destroy(struct xfrm_state *x) ...@@ -556,7 +540,7 @@ void __xfrm_state_destroy(struct xfrm_state *x)
WARN_ON(x->km.state != XFRM_STATE_DEAD); WARN_ON(x->km.state != XFRM_STATE_DEAD);
spin_lock_bh(&xfrm_state_gc_lock); spin_lock_bh(&xfrm_state_gc_lock);
list_add_tail(&x->gclist, &xfrm_state_gc_list); hlist_add_head(&x->gclist, &xfrm_state_gc_list);
spin_unlock_bh(&xfrm_state_gc_lock); spin_unlock_bh(&xfrm_state_gc_lock);
schedule_work(&xfrm_state_gc_work); schedule_work(&xfrm_state_gc_work);
} }
...@@ -569,8 +553,7 @@ int __xfrm_state_delete(struct xfrm_state *x) ...@@ -569,8 +553,7 @@ int __xfrm_state_delete(struct xfrm_state *x)
if (x->km.state != XFRM_STATE_DEAD) { if (x->km.state != XFRM_STATE_DEAD) {
x->km.state = XFRM_STATE_DEAD; x->km.state = XFRM_STATE_DEAD;
spin_lock(&xfrm_state_lock); spin_lock(&xfrm_state_lock);
x->lastused = xfrm_state_walk_ongoing; list_del(&x->km.all);
list_del_rcu(&x->all);
hlist_del(&x->bydst); hlist_del(&x->bydst);
hlist_del(&x->bysrc); hlist_del(&x->bysrc);
if (x->id.spi) if (x->id.spi)
...@@ -871,7 +854,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, ...@@ -871,7 +854,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
if (km_query(x, tmpl, pol) == 0) { if (km_query(x, tmpl, pol) == 0) {
x->km.state = XFRM_STATE_ACQ; x->km.state = XFRM_STATE_ACQ;
list_add_tail(&x->all, &xfrm_state_all); list_add(&x->km.all, &xfrm_state_all);
hlist_add_head(&x->bydst, xfrm_state_bydst+h); hlist_add_head(&x->bydst, xfrm_state_bydst+h);
h = xfrm_src_hash(daddr, saddr, family); h = xfrm_src_hash(daddr, saddr, family);
hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
...@@ -940,7 +923,7 @@ static void __xfrm_state_insert(struct xfrm_state *x) ...@@ -940,7 +923,7 @@ static void __xfrm_state_insert(struct xfrm_state *x)
x->genid = ++xfrm_state_genid; x->genid = ++xfrm_state_genid;
list_add_tail(&x->all, &xfrm_state_all); list_add(&x->km.all, &xfrm_state_all);
h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr, h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
x->props.reqid, x->props.family); x->props.reqid, x->props.family);
...@@ -1069,7 +1052,7 @@ static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 re ...@@ -1069,7 +1052,7 @@ static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 re
xfrm_state_hold(x); xfrm_state_hold(x);
x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ; x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
add_timer(&x->timer); add_timer(&x->timer);
list_add_tail(&x->all, &xfrm_state_all); list_add(&x->km.all, &xfrm_state_all);
hlist_add_head(&x->bydst, xfrm_state_bydst+h); hlist_add_head(&x->bydst, xfrm_state_bydst+h);
h = xfrm_src_hash(daddr, saddr, family); h = xfrm_src_hash(daddr, saddr, family);
hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
...@@ -1566,79 +1549,59 @@ int xfrm_state_walk(struct xfrm_state_walk *walk, ...@@ -1566,79 +1549,59 @@ int xfrm_state_walk(struct xfrm_state_walk *walk,
int (*func)(struct xfrm_state *, int, void*), int (*func)(struct xfrm_state *, int, void*),
void *data) void *data)
{ {
struct xfrm_state *old, *x, *last = NULL; struct xfrm_state *state;
struct xfrm_state_walk *x;
int err = 0; int err = 0;
if (walk->state == NULL && walk->count != 0) if (walk->seq != 0 && list_empty(&walk->all))
return 0; return 0;
old = x = walk->state;
walk->state = NULL;
spin_lock_bh(&xfrm_state_lock); spin_lock_bh(&xfrm_state_lock);
if (x == NULL) if (list_empty(&walk->all))
x = list_first_entry(&xfrm_state_all, struct xfrm_state, all); x = list_first_entry(&xfrm_state_all, struct xfrm_state_walk, all);
else
x = list_entry(&walk->all, struct xfrm_state_walk, all);
list_for_each_entry_from(x, &xfrm_state_all, all) { list_for_each_entry_from(x, &xfrm_state_all, all) {
if (x->km.state == XFRM_STATE_DEAD) if (x->state == XFRM_STATE_DEAD)
continue; continue;
if (!xfrm_id_proto_match(x->id.proto, walk->proto)) state = container_of(x, struct xfrm_state, km);
if (!xfrm_id_proto_match(state->id.proto, walk->proto))
continue; continue;
if (last) { err = func(state, walk->seq, data);
err = func(last, walk->count, data); if (err) {
if (err) { list_move_tail(&walk->all, &x->all);
xfrm_state_hold(last); goto out;
walk->state = last;
goto out;
}
} }
last = x; walk->seq++;
walk->count++;
} }
if (walk->count == 0) { if (walk->seq == 0) {
err = -ENOENT; err = -ENOENT;
goto out; goto out;
} }
if (last) list_del_init(&walk->all);
err = func(last, 0, data);
out: out:
spin_unlock_bh(&xfrm_state_lock); spin_unlock_bh(&xfrm_state_lock);
if (old != NULL)
xfrm_state_put(old);
return err; return err;
} }
EXPORT_SYMBOL(xfrm_state_walk); EXPORT_SYMBOL(xfrm_state_walk);
void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
{ {
INIT_LIST_HEAD(&walk->all);
walk->proto = proto; walk->proto = proto;
walk->state = NULL; walk->state = XFRM_STATE_DEAD;
walk->count = 0; walk->seq = 0;
list_add_tail(&walk->list, &xfrm_state_walks);
walk->genid = ++xfrm_state_walk_ongoing;
} }
EXPORT_SYMBOL(xfrm_state_walk_init); EXPORT_SYMBOL(xfrm_state_walk_init);
void xfrm_state_walk_done(struct xfrm_state_walk *walk) void xfrm_state_walk_done(struct xfrm_state_walk *walk)
{ {
struct list_head *prev; if (list_empty(&walk->all))
if (walk->state != NULL) {
xfrm_state_put(walk->state);
walk->state = NULL;
}
prev = walk->list.prev;
list_del(&walk->list);
if (prev != &xfrm_state_walks) {
list_entry(prev, struct xfrm_state_walk, list)->genid =
walk->genid;
return; return;
}
xfrm_state_walk_completed = walk->genid;
if (!list_empty(&xfrm_state_gc_leftovers)) spin_lock_bh(&xfrm_state_lock);
schedule_work(&xfrm_state_gc_work); list_del(&walk->all);
spin_lock_bh(&xfrm_state_lock);
} }
EXPORT_SYMBOL(xfrm_state_walk_done); EXPORT_SYMBOL(xfrm_state_walk_done);
......
...@@ -1102,7 +1102,7 @@ static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p, ...@@ -1102,7 +1102,7 @@ static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p,
return xp; return xp;
error: error:
*errp = err; *errp = err;
xp->dead = 1; xp->walk.dead = 1;
xfrm_policy_destroy(xp); xfrm_policy_destroy(xp);
return NULL; return NULL;
} }
...@@ -1595,7 +1595,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1595,7 +1595,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
return -ENOENT; return -ENOENT;
read_lock(&xp->lock); read_lock(&xp->lock);
if (xp->dead) { if (xp->walk.dead) {
read_unlock(&xp->lock); read_unlock(&xp->lock);
goto out; goto out;
} }
......
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