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,