aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2010-03-30 20:17:05 -0400
committerDavid S. Miller <davem@davemloft.net>2010-04-01 22:41:35 -0400
commitea2dea9dacc256fe927857feb423872051642ae7 (patch)
tree507da54eb46680b833ead15c3e74f9312cf10c22 /net
parentc8bf4d04f970fafb3430d332533e1cf103f2a018 (diff)
xfrm: remove policy lock when accessing policy->walk.dead
All of the code considers ->dead as a hint that the cached policy needs to get refreshed. The read side can just drop the read lock without any side effects. The write side needs to make sure that it's written only exactly once. Only possible race is at xfrm_policy_kill(). This is fixed by checking result of __xfrm_policy_unlink() when needed. It will always succeed if the policy object is looked up from the hash list (so some checks are removed), but it needs to be checked if we are trying to unlink policy via a reference (appropriate checks added). Since policy->walk.dead is written exactly once, it no longer needs to be protected with a write lock. Signed-off-by: Timo Teras <timo.teras@iki.fi> Acked-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/xfrm/xfrm_policy.c31
-rw-r--r--net/xfrm/xfrm_user.c6
2 files changed, 10 insertions, 27 deletions
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 843e066649cb..82789cf1c632 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -156,7 +156,7 @@ static void xfrm_policy_timer(unsigned long data)
156 156
157 read_lock(&xp->lock); 157 read_lock(&xp->lock);
158 158
159 if (xp->walk.dead) 159 if (unlikely(xp->walk.dead))
160 goto out; 160 goto out;
161 161
162 dir = xfrm_policy_id2dir(xp->index); 162 dir = xfrm_policy_id2dir(xp->index);
@@ -297,17 +297,7 @@ static DECLARE_WORK(xfrm_policy_gc_work, xfrm_policy_gc_task);
297 297
298static void xfrm_policy_kill(struct xfrm_policy *policy) 298static void xfrm_policy_kill(struct xfrm_policy *policy)
299{ 299{
300 int dead;
301
302 write_lock_bh(&policy->lock);
303 dead = policy->walk.dead;
304 policy->walk.dead = 1; 300 policy->walk.dead = 1;
305 write_unlock_bh(&policy->lock);
306
307 if (unlikely(dead)) {
308 WARN_ON(1);
309 return;
310 }
311 301
312 spin_lock_bh(&xfrm_policy_gc_lock); 302 spin_lock_bh(&xfrm_policy_gc_lock);
313 hlist_add_head(&policy->bydst, &xfrm_policy_gc_list); 303 hlist_add_head(&policy->bydst, &xfrm_policy_gc_list);
@@ -776,7 +766,6 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi
776int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) 766int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
777{ 767{
778 int dir, err = 0, cnt = 0; 768 int dir, err = 0, cnt = 0;
779 struct xfrm_policy *dp;
780 769
781 write_lock_bh(&xfrm_policy_lock); 770 write_lock_bh(&xfrm_policy_lock);
782 771
@@ -794,10 +783,9 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
794 &net->xfrm.policy_inexact[dir], bydst) { 783 &net->xfrm.policy_inexact[dir], bydst) {
795 if (pol->type != type) 784 if (pol->type != type)
796 continue; 785 continue;
797 dp = __xfrm_policy_unlink(pol, dir); 786 __xfrm_policy_unlink(pol, dir);
798 write_unlock_bh(&xfrm_policy_lock); 787 write_unlock_bh(&xfrm_policy_lock);
799 if (dp) 788 cnt++;
800 cnt++;
801 789
802 xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, 790 xfrm_audit_policy_delete(pol, 1, audit_info->loginuid,
803 audit_info->sessionid, 791 audit_info->sessionid,
@@ -816,10 +804,9 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
816 bydst) { 804 bydst) {
817 if (pol->type != type) 805 if (pol->type != type)
818 continue; 806 continue;
819 dp = __xfrm_policy_unlink(pol, dir); 807 __xfrm_policy_unlink(pol, dir);
820 write_unlock_bh(&xfrm_policy_lock); 808 write_unlock_bh(&xfrm_policy_lock);
821 if (dp) 809 cnt++;
822 cnt++;
823 810
824 xfrm_audit_policy_delete(pol, 1, 811 xfrm_audit_policy_delete(pol, 1,
825 audit_info->loginuid, 812 audit_info->loginuid,
@@ -1132,6 +1119,9 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
1132 __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir); 1119 __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir);
1133 } 1120 }
1134 if (old_pol) 1121 if (old_pol)
1122 /* Unlinking succeeds always. This is the only function
1123 * allowed to delete or replace socket policy.
1124 */
1135 __xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir); 1125 __xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir);
1136 write_unlock_bh(&xfrm_policy_lock); 1126 write_unlock_bh(&xfrm_policy_lock);
1137 1127
@@ -1737,11 +1727,8 @@ restart:
1737 goto error; 1727 goto error;
1738 } 1728 }
1739 1729
1740 for (pi = 0; pi < npols; pi++) { 1730 for (pi = 0; pi < npols; pi++)
1741 read_lock_bh(&pols[pi]->lock);
1742 pol_dead |= pols[pi]->walk.dead; 1731 pol_dead |= pols[pi]->walk.dead;
1743 read_unlock_bh(&pols[pi]->lock);
1744 }
1745 1732
1746 write_lock_bh(&policy->lock); 1733 write_lock_bh(&policy->lock);
1747 if (unlikely(pol_dead || stale_bundle(dst))) { 1734 if (unlikely(pol_dead || stale_bundle(dst))) {
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index da5ba86181de..a267fbdda525 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1770,13 +1770,9 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
1770 if (xp == NULL) 1770 if (xp == NULL)
1771 return -ENOENT; 1771 return -ENOENT;
1772 1772
1773 read_lock(&xp->lock); 1773 if (unlikely(xp->walk.dead))
1774 if (xp->walk.dead) {
1775 read_unlock(&xp->lock);
1776 goto out; 1774 goto out;
1777 }
1778 1775
1779 read_unlock(&xp->lock);
1780 err = 0; 1776 err = 0;
1781 if (up->hard) { 1777 if (up->hard) {
1782 uid_t loginuid = NETLINK_CB(skb).loginuid; 1778 uid_t loginuid = NETLINK_CB(skb).loginuid;