Input: fix regression when re-registering input handlers
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 28 Oct 2024 05:31:15 +0000 (22:31 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sun, 3 Nov 2024 05:28:58 +0000 (22:28 -0700)
Commit d469647bafd9 ("Input: simplify event handling logic") introduced
code that would set handler->events() method to either
input_handler_events_filter() or input_handler_events_default() or
input_handler_events_null(), depending on the kind of input handler
(a filter or a regular one) we are dealing with. Unfortunately this
breaks cases when we try to re-register the same filter (as is the case
with sysrq handler): after initial registration the handler will have 2
event handling methods defined, and will run afoul of the check in
input_handler_check_methods():

input: input_handler_check_methods: only one event processing method can be defined (sysrq)
sysrq: Failed to register input handler, error -22

Fix this by adding handle_events() method to input_handle structure and
setting it up when registering a new input handle according to event
handling methods defined in associated input_handler structure, thus
avoiding modifying the input_handler structure.

Reported-by: "Ned T. Crigler" <crigler@gmail.com>
Reported-by: Christian Heusel <christian@heusel.eu>
Tested-by: "Ned T. Crigler" <crigler@gmail.com>
Tested-by: Peter Seiderer <ps.report@gmx.net>
Fixes: d469647bafd9 ("Input: simplify event handling logic")
Link: https://lore.kernel.org/r/Zx2iQp6csn42PJA7@xavtug
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/input.c
include/linux/input.h

index 3c321671793f226071253da641f876e90da41a17..3d2cc13e1f32c89d293380b17f0adef2856d2a45 100644 (file)
@@ -119,12 +119,12 @@ static void input_pass_values(struct input_dev *dev,
 
        handle = rcu_dereference(dev->grab);
        if (handle) {
-               count = handle->handler->events(handle, vals, count);
+               count = handle->handle_events(handle, vals, count);
        } else {
                list_for_each_entry_rcu(handle, &dev->h_list, d_node)
                        if (handle->open) {
-                               count = handle->handler->events(handle, vals,
-                                                               count);
+                               count = handle->handle_events(handle, vals,
+                                                             count);
                                if (!count)
                                        break;
                        }
@@ -2537,57 +2537,6 @@ static int input_handler_check_methods(const struct input_handler *handler)
        return 0;
 }
 
-/*
- * An implementation of input_handler's events() method that simply
- * invokes handler->event() method for each event one by one.
- */
-static unsigned int input_handler_events_default(struct input_handle *handle,
-                                                struct input_value *vals,
-                                                unsigned int count)
-{
-       struct input_handler *handler = handle->handler;
-       struct input_value *v;
-
-       for (v = vals; v != vals + count; v++)
-               handler->event(handle, v->type, v->code, v->value);
-
-       return count;
-}
-
-/*
- * An implementation of input_handler's events() method that invokes
- * handler->filter() method for each event one by one and removes events
- * that were filtered out from the "vals" array.
- */
-static unsigned int input_handler_events_filter(struct input_handle *handle,
-                                               struct input_value *vals,
-                                               unsigned int count)
-{
-       struct input_handler *handler = handle->handler;
-       struct input_value *end = vals;
-       struct input_value *v;
-
-       for (v = vals; v != vals + count; v++) {
-               if (handler->filter(handle, v->type, v->code, v->value))
-                       continue;
-               if (end != v)
-                       *end = *v;
-               end++;
-       }
-
-       return end - vals;
-}
-
-/*
- * An implementation of input_handler's events() method that does nothing.
- */
-static unsigned int input_handler_events_null(struct input_handle *handle,
-                                             struct input_value *vals,
-                                             unsigned int count)
-{
-       return count;
-}
-
 /**
  * input_register_handler - register a new input handler
  * @handler: handler to be registered
@@ -2607,13 +2556,6 @@ int input_register_handler(struct input_handler *handler)
 
        INIT_LIST_HEAD(&handler->h_list);
 
-       if (handler->filter)
-               handler->events = input_handler_events_filter;
-       else if (handler->event)
-               handler->events = input_handler_events_default;
-       else if (!handler->events)
-               handler->events = input_handler_events_null;
-
        error = mutex_lock_interruptible(&input_mutex);
        if (error)
                return error;
@@ -2687,6 +2629,75 @@ int input_handler_for_each_handle(struct input_handler *handler, void *data,
 }
 EXPORT_SYMBOL(input_handler_for_each_handle);
 
+/*
+ * An implementation of input_handle's handle_events() method that simply
+ * invokes handler->event() method for each event one by one.
+ */
+static unsigned int input_handle_events_default(struct input_handle *handle,
+                                               struct input_value *vals,
+                                               unsigned int count)
+{
+       struct input_handler *handler = handle->handler;
+       struct input_value *v;
+
+       for (v = vals; v != vals + count; v++)
+               handler->event(handle, v->type, v->code, v->value);
+
+       return count;
+}
+
+/*
+ * An implementation of input_handle's handle_events() method that invokes
+ * handler->filter() method for each event one by one and removes events
+ * that were filtered out from the "vals" array.
+ */
+static unsigned int input_handle_events_filter(struct input_handle *handle,
+                                              struct input_value *vals,
+                                              unsigned int count)
+{
+       struct input_handler *handler = handle->handler;
+       struct input_value *end = vals;
+       struct input_value *v;
+
+       for (v = vals; v != vals + count; v++) {
+               if (handler->filter(handle, v->type, v->code, v->value))
+                       continue;
+               if (end != v)
+                       *end = *v;
+               end++;
+       }
+
+       return end - vals;
+}
+
+/*
+ * An implementation of input_handle's handle_events() method that does nothing.
+ */
+static unsigned int input_handle_events_null(struct input_handle *handle,
+                                            struct input_value *vals,
+                                            unsigned int count)
+{
+       return count;
+}
+
+/*
+ * Sets up appropriate handle->event_handler based on the input_handler
+ * associated with the handle.
+ */
+static void input_handle_setup_event_handler(struct input_handle *handle)
+{
+       struct input_handler *handler = handle->handler;
+
+       if (handler->filter)
+               handle->handle_events = input_handle_events_filter;
+       else if (handler->event)
+               handle->handle_events = input_handle_events_default;
+       else if (handler->events)
+               handle->handle_events = handler->events;
+       else
+               handle->handle_events = input_handle_events_null;
+}
+
 /**
  * input_register_handle - register a new input handle
  * @handle: handle to register
@@ -2704,6 +2715,7 @@ int input_register_handle(struct input_handle *handle)
        struct input_dev *dev = handle->dev;
        int error;
 
+       input_handle_setup_event_handler(handle);
        /*
         * We take dev->mutex here to prevent race with
         * input_release_device().
index 89a0be6ee0e23be40dcc7a268bdaf58a91b03aa9..cd866b020a01d67081788e1baa2d372ce1c79767 100644 (file)
@@ -339,12 +339,16 @@ struct input_handler {
  * @name: name given to the handle by handler that created it
  * @dev: input device the handle is attached to
  * @handler: handler that works with the device through this handle
+ * @handle_events: event sequence handler. It is set up by the input core
+ *     according to event handling method specified in the @handler. See
+ *     input_handle_setup_event_handler().
+ *     This method is being called by the input core with interrupts disabled
+ *     and dev->event_lock spinlock held and so it may not sleep.
  * @d_node: used to put the handle on device's list of attached handles
  * @h_node: used to put the handle on handler's list of handles from which
  *     it gets events
  */
 struct input_handle {
-
        void *private;
 
        int open;
@@ -353,6 +357,10 @@ struct input_handle {
        struct input_dev *dev;
        struct input_handler *handler;
 
+       unsigned int (*handle_events)(struct input_handle *handle,
+                                     struct input_value *vals,
+                                     unsigned int count);
+
        struct list_head        d_node;
        struct list_head        h_node;
 };