diff options
author | Szymon Janc <szymon.janc@tieto.com> | 2012-09-26 08:17:12 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2012-09-27 04:52:17 -0400 |
commit | 3c0cc8aa23f4b53446bbf385d4647eec6992a2cb (patch) | |
tree | 0f11703fb7a18c9130f3a62a8edb417184dabe2a /net | |
parent | 7d777c3d95a18ae42473c63104d87ad080885094 (diff) |
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')
-rw-r--r-- | net/nfc/netlink.c | 46 |
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[] = { | |||
761 | }, | 761 | }, |
762 | }; | 762 | }; |
763 | 763 | ||
764 | static int nfc_genl_rcv_nl_event(struct notifier_block *this, | 764 | |
765 | unsigned long event, void *ptr) | 765 | struct urelease_work { |
766 | struct work_struct w; | ||
767 | int pid; | ||
768 | }; | ||
769 | |||
770 | static void nfc_urelease_event_work(struct work_struct *work) | ||
766 | { | 771 | { |
767 | struct netlink_notify *n = ptr; | 772 | struct urelease_work *w = container_of(work, struct urelease_work, w); |
768 | struct class_dev_iter iter; | 773 | struct class_dev_iter iter; |
769 | struct nfc_dev *dev; | 774 | struct nfc_dev *dev; |
770 | 775 | ||
771 | if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC) | 776 | pr_debug("pid %d\n", w->pid); |
772 | goto out; | ||
773 | 777 | ||
774 | pr_debug("NETLINK_URELEASE event from id %d\n", n->pid); | 778 | mutex_lock(&nfc_devlist_mutex); |
775 | 779 | ||
776 | nfc_device_iter_init(&iter); | 780 | nfc_device_iter_init(&iter); |
777 | dev = nfc_device_iter_next(&iter); | 781 | dev = nfc_device_iter_next(&iter); |
778 | 782 | ||
779 | while (dev) { | 783 | while (dev) { |
780 | if (dev->genl_data.poll_req_pid == n->pid) { | 784 | mutex_lock(&dev->genl_data.genl_data_mutex); |
785 | |||
786 | if (dev->genl_data.poll_req_pid == w->pid) { | ||
781 | nfc_stop_poll(dev); | 787 | nfc_stop_poll(dev); |
782 | dev->genl_data.poll_req_pid = 0; | 788 | dev->genl_data.poll_req_pid = 0; |
783 | } | 789 | } |
790 | |||
791 | mutex_unlock(&dev->genl_data.genl_data_mutex); | ||
792 | |||
784 | dev = nfc_device_iter_next(&iter); | 793 | dev = nfc_device_iter_next(&iter); |
785 | } | 794 | } |
786 | 795 | ||
787 | nfc_device_iter_exit(&iter); | 796 | nfc_device_iter_exit(&iter); |
788 | 797 | ||
798 | mutex_unlock(&nfc_devlist_mutex); | ||
799 | |||
800 | kfree(w); | ||
801 | } | ||
802 | |||
803 | static int nfc_genl_rcv_nl_event(struct notifier_block *this, | ||
804 | unsigned long event, void *ptr) | ||
805 | { | ||
806 | struct netlink_notify *n = ptr; | ||
807 | struct urelease_work *w; | ||
808 | |||
809 | if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC) | ||
810 | goto out; | ||
811 | |||
812 | pr_debug("NETLINK_URELEASE event from id %d\n", n->pid); | ||
813 | |||
814 | w = kmalloc(sizeof(*w), GFP_ATOMIC); | ||
815 | if (w) { | ||
816 | INIT_WORK((struct work_struct *) w, nfc_urelease_event_work); | ||
817 | w->pid = n->pid; | ||
818 | schedule_work((struct work_struct *) w); | ||
819 | } | ||
820 | |||
789 | out: | 821 | out: |
790 | return NOTIFY_DONE; | 822 | return NOTIFY_DONE; |
791 | } | 823 | } |