Bluetooth: Change socket release context

When sock_put() is called on an L2CAP socket, if the reference count
on a sock goes to 0, l2cap_sock_release() is called. As part of the
release, l2cap_sock_shutdown() locks the socket. This is not allowable
in interrupt context.  This change makes calls to sock_put() on the
system workqueue, where it can safely lock.

This addresses "scheduling while atomic" issues in both the Bluetooth
mgmt_ops pairing code and when RFCOMM sessions are deleted in a
security_cfm callback.

Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 72d249e..424f2df 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -58,6 +58,11 @@
 	void *user_data;
 };
 
+struct mgmt_pending_free_work {
+	struct work_struct work;
+	struct sock *sk;
+};
+
 LIST_HEAD(cmd_list);
 
 static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
@@ -238,13 +243,35 @@
 	return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
 }
 
+static void mgmt_pending_free_worker(struct work_struct *work)
+{
+	struct mgmt_pending_free_work *free_work =
+		container_of(work, struct mgmt_pending_free_work, work);
+
+	BT_DBG("sk %p", free_work->sk);
+
+	sock_put(free_work->sk);
+	kfree(free_work);
+}
+
 static void mgmt_pending_free(struct pending_cmd *cmd)
 {
-	BT_DBG("%d", cmd->opcode);
+	struct mgmt_pending_free_work *free_work;
+	struct sock *sk = cmd->sk;
 
-	sock_put(cmd->sk);
+	BT_DBG("opcode %d, sk %p", cmd->opcode, sk);
+
 	kfree(cmd->param);
 	kfree(cmd);
+
+	free_work = kzalloc(sizeof(*free_work), GFP_ATOMIC);
+	if (free_work) {
+		INIT_WORK(&free_work->work, mgmt_pending_free_worker);
+		free_work->sk = sk;
+
+		if (!schedule_work(&free_work->work))
+			kfree(free_work);
+	}
 }
 
 static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 7ab6382..16bb5ea 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -121,6 +121,12 @@
 #define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
 #define __get_rpn_parity(line)    (((line) >> 3) & 0x7)
 
+struct rfcomm_sock_release_work {
+	struct work_struct work;
+	struct socket *sock;
+	int state;
+};
+
 static inline void rfcomm_schedule(void)
 {
 	if (!rfcomm_thread)
@@ -630,9 +636,25 @@
 	return s;
 }
 
+static void rfcomm_sock_release_worker(struct work_struct *work)
+{
+	struct rfcomm_sock_release_work *release_work =
+		container_of(work, struct rfcomm_sock_release_work, work);
+
+	BT_DBG("sock %p", release_work->sock);
+
+	sock_release(release_work->sock);
+	if (release_work->state != BT_LISTEN)
+		module_put(THIS_MODULE);
+
+	kfree(release_work);
+}
+
 static void rfcomm_session_del(struct rfcomm_session *s)
 {
 	int state = s->state;
+	struct socket *sock = s->sock;
+	struct rfcomm_sock_release_work *release_work;
 
 	BT_DBG("session %p state %ld", s, s->state);
 
@@ -642,11 +664,19 @@
 		rfcomm_send_disc(s, 0);
 
 	rfcomm_session_clear_timer(s);
-	sock_release(s->sock);
+
 	kfree(s);
 
-	if (state != BT_LISTEN)
-		module_put(THIS_MODULE);
+	release_work = kzalloc(sizeof(*release_work), GFP_ATOMIC);
+	if (release_work) {
+		INIT_WORK(&release_work->work, rfcomm_sock_release_worker);
+		release_work->sock = sock;
+		release_work->state = state;
+
+		if (!schedule_work(&release_work->work))
+			kfree(release_work);
+	}
+
 }
 
 static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)