aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/hidp/core.c
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2013-05-23 07:10:25 -0400
committerJiri Kosina <jkosina@suse.cz>2013-05-29 09:19:11 -0400
commit4e713cdffba8d486e58eefe2125041eb5df9aa3a (patch)
tree7eebeace3957cfb1b8d0e7725ae0e08ed97cc6bc /net/bluetooth/hidp/core.c
parentf755407dd19072b7d20719bc5454caed9ab41cc1 (diff)
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even if we would add more fine-grained locking to HCI core, it would still be called from the non-reentrant rx work-queue and thus block the event processing. However, if we want to perform synchronous I/O during HID device registration (eg., to perform device-detection), we need the HCI core to be able to dispatch incoming data. Therefore, we now move device-registration to a separate worker. The HCI core can continue running and we add devices asynchronously in another kernel thread. Device removal is synchronized and waits for the worker to exit before calling the usual device removal functions. If l2cap_user->remove is called before the thread registered the devices, we set "terminate" to true and the thread will skip it. If l2cap_user->remove is called after it, we notice this as the device is no longer in HIDP_SESSION_PREPARING state and simply unregister the device as we did before. There is no new deadlock as we now call hidp_session_add_dev() with one lock less held (the HCI lock) and it cannot itself call back into HCI as it was called with the HCI-lock held before. One might wonder whether this can block during device unregistration. But we set "terminate" to true and wake the HIDP thread up _before_ unregistering the HID/input devices. Therefore, all pending HID I/O operations are canceled. All further I/O attempts will fail with ENODEV or EIO. So all latency we can get are few context-switches, but no timeouts or blocking I/O waits! This change also prepares for a long standing HID bug. All HID devices that register power_supply devices need to be able to handle callbacks during registration (a power_supply oddity that cannot easily be fixed). So with this patch available, we can allow HID I/O during registration by calling the recently introduced hid_device_io_start/stop helpers, which currently are a no-op for bluetooth due to this locking. Note that we cannot do the same for input devices. input-core doesn't allow us to call input_event() asynchronously to input_register_device(), which HID-core kindly allows (for good reasons). Fixing input-core to allow this isn't as easy as it sounds and is, beside simplifying HIDP, not really an improvement. Hence, we still register input devices synchronously as we did before. Only HID devices are registered asynchronously. Signed-off-by: David Herrmann <dh.herrmann@gmail.com> Acked-by: Jiri Kosina <jkosina@suse.cz> Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk> Tested-by: Daniel Nicoletti <dantti12@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'net/bluetooth/hidp/core.c')
-rw-r--r--net/bluetooth/hidp/core.c56
1 files changed, 47 insertions, 9 deletions
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 940f5acb6694..46c6a148f0b3 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -851,6 +851,29 @@ static void hidp_session_dev_del(struct hidp_session *session)
851} 851}
852 852
853/* 853/*
854 * Asynchronous device registration
855 * HID device drivers might want to perform I/O during initialization to
856 * detect device types. Therefore, call device registration in a separate
857 * worker so the HIDP thread can schedule I/O operations.
858 * Note that this must be called after the worker thread was initialized
859 * successfully. This will then add the devices and increase session state
860 * on success, otherwise it will terminate the session thread.
861 */
862static void hidp_session_dev_work(struct work_struct *work)
863{
864 struct hidp_session *session = container_of(work,
865 struct hidp_session,
866 dev_init);
867 int ret;
868
869 ret = hidp_session_dev_add(session);
870 if (!ret)
871 atomic_inc(&session->state);
872 else
873 hidp_session_terminate(session);
874}
875
876/*
854 * Create new session object 877 * Create new session object
855 * Allocate session object, initialize static fields, copy input data into the 878 * Allocate session object, initialize static fields, copy input data into the
856 * object and take a reference to all sub-objects. 879 * object and take a reference to all sub-objects.
@@ -897,6 +920,7 @@ static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr,
897 session->idle_to = req->idle_to; 920 session->idle_to = req->idle_to;
898 921
899 /* device management */ 922 /* device management */
923 INIT_WORK(&session->dev_init, hidp_session_dev_work);
900 setup_timer(&session->timer, hidp_idle_timeout, 924 setup_timer(&session->timer, hidp_idle_timeout,
901 (unsigned long)session); 925 (unsigned long)session);
902 926
@@ -1035,8 +1059,8 @@ static void hidp_session_terminate(struct hidp_session *session)
1035 * Probe HIDP session 1059 * Probe HIDP session
1036 * This is called from the l2cap_conn core when our l2cap_user object is bound 1060 * This is called from the l2cap_conn core when our l2cap_user object is bound
1037 * to the hci-connection. We get the session via the \user object and can now 1061 * to the hci-connection. We get the session via the \user object and can now
1038 * start the session thread, register the HID/input devices and link it into 1062 * start the session thread, link it into the global session list and
1039 * the global session list. 1063 * schedule HID/input device registration.
1040 * The global session-list owns its own reference to the session object so you 1064 * The global session-list owns its own reference to the session object so you
1041 * can drop your own reference after registering the l2cap_user object. 1065 * can drop your own reference after registering the l2cap_user object.
1042 */ 1066 */
@@ -1058,21 +1082,30 @@ static int hidp_session_probe(struct l2cap_conn *conn,
1058 goto out_unlock; 1082 goto out_unlock;
1059 } 1083 }
1060 1084
1085 if (session->input) {
1086 ret = hidp_session_dev_add(session);
1087 if (ret)
1088 goto out_unlock;
1089 }
1090
1061 ret = hidp_session_start_sync(session); 1091 ret = hidp_session_start_sync(session);
1062 if (ret) 1092 if (ret)
1063 goto out_unlock; 1093 goto out_del;
1064 1094
1065 ret = hidp_session_dev_add(session); 1095 /* HID device registration is async to allow I/O during probe */
1066 if (ret) 1096 if (session->input)
1067 goto out_stop; 1097 atomic_inc(&session->state);
1098 else
1099 schedule_work(&session->dev_init);
1068 1100
1069 hidp_session_get(session); 1101 hidp_session_get(session);
1070 list_add(&session->list, &hidp_session_list); 1102 list_add(&session->list, &hidp_session_list);
1071 ret = 0; 1103 ret = 0;
1072 goto out_unlock; 1104 goto out_unlock;
1073 1105
1074out_stop: 1106out_del:
1075 hidp_session_terminate(session); 1107 if (session->input)
1108 hidp_session_dev_del(session);
1076out_unlock: 1109out_unlock:
1077 up_write(&hidp_session_sem); 1110 up_write(&hidp_session_sem);
1078 return ret; 1111 return ret;
@@ -1102,7 +1135,12 @@ static void hidp_session_remove(struct l2cap_conn *conn,
1102 down_write(&hidp_session_sem); 1135 down_write(&hidp_session_sem);
1103 1136
1104 hidp_session_terminate(session); 1137 hidp_session_terminate(session);
1105 hidp_session_dev_del(session); 1138
1139 cancel_work_sync(&session->dev_init);
1140 if (session->input ||
1141 atomic_read(&session->state) > HIDP_SESSION_PREPARING)
1142 hidp_session_dev_del(session);
1143
1106 list_del(&session->list); 1144 list_del(&session->list);
1107 1145
1108 up_write(&hidp_session_sem); 1146 up_write(&hidp_session_sem);