From: Szymon Janc Date: Wed, 26 Sep 2012 12:17:12 +0000 (+0200) Subject: NFC: Fix sleeping in invalid context when netlink socket is closed X-Git-Tag: v3.7-rc1~145^2~30^2~39^2~1 X-Git-Url: http://git.cascardo.eti.br/?a=commitdiff_plain;h=3c0cc8aa23f4b53446bbf385d4647eec6992a2cb;p=cascardo%2Flinux.git 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: [] __might_sleep+0x145/0x200 [] mutex_lock_nested+0x2e/0x50 [] nfc_stop_poll+0x39/0xb0 [] nfc_genl_rcv_nl_event+0x77/0xc0 [] notifier_call_chain+0x5c/0x120 [] __atomic_notifier_call_chain+0x86/0x140 [] ? notifier_call_chain+0x120/0x120 [] ? skb_dequeue+0x67/0x90 [] atomic_notifier_call_chain+0x16/0x20 [] netlink_release+0x24a/0x280 [] sock_release+0x28/0xa0 [] sock_close+0x17/0x30 [] __fput+0xcc/0x250 [] ____fput+0xe/0x10 [] task_work_run+0x69/0x90 [] do_notify_resume+0x81/0xd0 [] int_signal+0x12/0x17 Signed-off-by: Szymon Janc Signed-off-by: Samuel Ortiz --- 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; }