From 97aca38ddd3421bb7f13bf1c41566b23429906ae Mon Sep 17 00:00:00 2001
From: JP Abgrall <jpa@google.com>
Date: Thu, 18 Aug 2011 15:05:47 -0700
Subject: [PATCH] netfilter: xt_qtaguid: Fix sockfd_put() call within spinlock

sockfd_put() risks sleeping.
So when doing a delete ctrl command, defer the sockfd_put() and
kfree() to outside of the spinlock.

Change-Id: I5f8ab51d05888d885b2fbb035f61efa5b7abb88a
Signed-off-by: JP Abgrall <jpa@google.com>
---
 net/netfilter/xt_qtaguid.c | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index b9dcfde99762..968693cb1bc0 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -1325,6 +1325,7 @@ static int ctrl_cmd_delete(const char *input)
 	struct iface_stat *iface_entry;
 	struct rb_node *node;
 	struct sock_tag *st_entry;
+	struct rb_root st_to_free_tree = RB_ROOT;
 	struct tag_stat *ts_entry;
 	struct tag_counter_set *tcs_entry;
 
@@ -1362,18 +1363,27 @@ static int ctrl_cmd_delete(const char *input)
 			continue;
 
 		if (!acct_tag || st_entry->tag == tag) {
-			CT_DEBUG("qtaguid: ctrl_delete(): "
-				 "erase st: sk=%p tag=0x%llx (uid=%u)\n",
-				 st_entry->sk,
-				 st_entry->tag,
-				 entry_uid);
 			rb_erase(&st_entry->sock_node, &sock_tag_tree);
-			sockfd_put(st_entry->socket);
-			kfree(st_entry);
+			/* Can't sockfd_put() within spinlock, do it later. */
+			sock_tag_tree_insert(st_entry, &st_to_free_tree);
 		}
 	}
 	spin_unlock_bh(&sock_tag_list_lock);
 
+	node = rb_first(&st_to_free_tree);
+	while (node) {
+		st_entry = rb_entry(node, struct sock_tag, sock_node);
+		node = rb_next(node);
+		CT_DEBUG("qtaguid: ctrl_delete(): "
+			 "erase st: sk=%p tag=0x%llx (uid=%u)\n",
+			 st_entry->sk,
+			 st_entry->tag,
+			 entry_uid);
+		rb_erase(&st_entry->sock_node, &st_to_free_tree);
+		sockfd_put(st_entry->socket);
+		kfree(st_entry);
+	}
+
 	tag = combine_atag_with_uid(acct_tag, uid);
 
 	/* Delete tag counter-sets */
-- 
2.34.1