summaryrefslogtreecommitdiff
path: root/net/nfc
diff options
context:
space:
mode:
authorSzymon Janc <szymon.janc@tieto.com>2012-09-26 16:17:12 +0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-09-27 12:52:17 +0400
commit3c0cc8aa23f4b53446bbf385d4647eec6992a2cb (patch)
tree0f11703fb7a18c9130f3a62a8edb417184dabe2a /net/nfc
parent7d777c3d95a18ae42473c63104d87ad080885094 (diff)
downloadlinux-3c0cc8aa23f4b53446bbf385d4647eec6992a2cb.tar.xz
NFC: Fix sleeping in invalid context when netlink socket is closed
netlink_register_notifier requires notify functions to not sleep. nfc_stop_poll locks device mutex and must not be called from notifier. Create workqueue that will handle this for all devices. BUG: sleeping function called from invalid context at kernel/mutex.c:269 in_atomic(): 0, irqs_disabled(): 0, pid: 4497, name: neard 1 lock held by neard/4497: Pid: 4497, comm: neard Not tainted 3.5.0-999-nfc+ #5 Call Trace: [<ffffffff810952c5>] __might_sleep+0x145/0x200 [<ffffffff81743dde>] mutex_lock_nested+0x2e/0x50 [<ffffffff816ffd19>] nfc_stop_poll+0x39/0xb0 [<ffffffff81700a17>] nfc_genl_rcv_nl_event+0x77/0xc0 [<ffffffff8174aa8c>] notifier_call_chain+0x5c/0x120 [<ffffffff8174abd6>] __atomic_notifier_call_chain+0x86/0x140 [<ffffffff8174ab50>] ? notifier_call_chain+0x120/0x120 [<ffffffff815e1347>] ? skb_dequeue+0x67/0x90 [<ffffffff8174aca6>] atomic_notifier_call_chain+0x16/0x20 [<ffffffff8162119a>] netlink_release+0x24a/0x280 [<ffffffff815d7aa8>] sock_release+0x28/0xa0 [<ffffffff815d7be7>] sock_close+0x17/0x30 [<ffffffff811b2a7c>] __fput+0xcc/0x250 [<ffffffff811b2c0e>] ____fput+0xe/0x10 [<ffffffff81085009>] task_work_run+0x69/0x90 [<ffffffff8101b951>] do_notify_resume+0x81/0xd0 [<ffffffff8174ef22>] int_signal+0x12/0x17 Signed-off-by: Szymon Janc <szymon.janc@tieto.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'net/nfc')
-rw-r--r--net/nfc/netlink.c46
1 files changed, 39 insertions, 7 deletions
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 4c51714ee741..baa6af9500df 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -761,31 +761,63 @@ static struct genl_ops nfc_genl_ops[] = {
},
};
-static int nfc_genl_rcv_nl_event(struct notifier_block *this,
- unsigned long event, void *ptr)
+
+struct urelease_work {
+ struct work_struct w;
+ int pid;
+};
+
+static void nfc_urelease_event_work(struct work_struct *work)
{
- struct netlink_notify *n = ptr;
+ struct urelease_work *w = container_of(work, struct urelease_work, w);
struct class_dev_iter iter;
struct nfc_dev *dev;
- if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
- goto out;
+ pr_debug("pid %d\n", w->pid);
- pr_debug("NETLINK_URELEASE event from id %d\n", n->pid);
+ mutex_lock(&nfc_devlist_mutex);
nfc_device_iter_init(&iter);
dev = nfc_device_iter_next(&iter);
while (dev) {
- if (dev->genl_data.poll_req_pid == n->pid) {
+ mutex_lock(&dev->genl_data.genl_data_mutex);
+
+ if (dev->genl_data.poll_req_pid == w->pid) {
nfc_stop_poll(dev);
dev->genl_data.poll_req_pid = 0;
}
+
+ mutex_unlock(&dev->genl_data.genl_data_mutex);
+
dev = nfc_device_iter_next(&iter);
}
nfc_device_iter_exit(&iter);
+ mutex_unlock(&nfc_devlist_mutex);
+
+ kfree(w);
+}
+
+static int nfc_genl_rcv_nl_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct netlink_notify *n = ptr;
+ struct urelease_work *w;
+
+ if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
+ goto out;
+
+ pr_debug("NETLINK_URELEASE event from id %d\n", n->pid);
+
+ w = kmalloc(sizeof(*w), GFP_ATOMIC);
+ if (w) {
+ INIT_WORK((struct work_struct *) w, nfc_urelease_event_work);
+ w->pid = n->pid;
+ schedule_work((struct work_struct *) w);
+ }
+
out:
return NOTIFY_DONE;
}