2024-12-10 00:17:37 +03:00
|
|
|
From 2f3e9fbc48151e4499f9cbd810d9467ac34b0a3b Mon Sep 17 00:00:00 2001
|
2024-10-29 05:12:06 +03:00
|
|
|
From: Kenny Levinsen <kl@kl.wtf>
|
|
|
|
Date: Sun, 27 Dec 2020 14:43:13 +0000
|
|
|
|
Subject: ZEN: Input: evdev - use call_rcu when detaching client
|
|
|
|
|
|
|
|
Significant time was spent on synchronize_rcu in evdev_detach_client
|
|
|
|
when applications closed evdev devices. Switching VT away from a
|
|
|
|
graphical environment commonly leads to mass input device closures,
|
|
|
|
which could lead to noticable delays on systems with many input devices.
|
|
|
|
|
|
|
|
Replace synchronize_rcu with call_rcu, deferring reclaim of the evdev
|
|
|
|
client struct till after the RCU grace period instead of blocking the
|
|
|
|
calling application.
|
|
|
|
|
|
|
|
While this does not solve all slow evdev fd closures, it takes care of a
|
|
|
|
good portion of them, including this simple test:
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int idx, fd;
|
|
|
|
const char *path = "/dev/input/event0";
|
|
|
|
for (idx = 0; idx < 1000; idx++) {
|
|
|
|
if ((fd = open(path, O_RDWR)) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Time to completion of above test when run locally:
|
|
|
|
|
|
|
|
Before: 0m27.111s
|
|
|
|
After: 0m0.018s
|
|
|
|
|
|
|
|
Signed-off-by: Kenny Levinsen <kl@kl.wtf>
|
|
|
|
---
|
|
|
|
drivers/input/evdev.c | 19 +++++++++++--------
|
|
|
|
1 file changed, 11 insertions(+), 8 deletions(-)
|
|
|
|
|
|
|
|
--- a/drivers/input/evdev.c
|
|
|
|
+++ b/drivers/input/evdev.c
|
|
|
|
@@ -46,6 +46,7 @@ struct evdev_client {
|
|
|
|
struct fasync_struct *fasync;
|
|
|
|
struct evdev *evdev;
|
|
|
|
struct list_head node;
|
|
|
|
+ struct rcu_head rcu;
|
|
|
|
enum input_clock_type clk_type;
|
|
|
|
bool revoked;
|
|
|
|
unsigned long *evmasks[EV_CNT];
|
|
|
|
@@ -368,13 +369,22 @@ static void evdev_attach_client(struct e
|
|
|
|
spin_unlock(&evdev->client_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
+static void evdev_reclaim_client(struct rcu_head *rp)
|
|
|
|
+{
|
|
|
|
+ struct evdev_client *client = container_of(rp, struct evdev_client, rcu);
|
|
|
|
+ unsigned int i;
|
|
|
|
+ for (i = 0; i < EV_CNT; ++i)
|
|
|
|
+ bitmap_free(client->evmasks[i]);
|
|
|
|
+ kvfree(client);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void evdev_detach_client(struct evdev *evdev,
|
|
|
|
struct evdev_client *client)
|
|
|
|
{
|
|
|
|
spin_lock(&evdev->client_lock);
|
|
|
|
list_del_rcu(&client->node);
|
|
|
|
spin_unlock(&evdev->client_lock);
|
|
|
|
- synchronize_rcu();
|
|
|
|
+ call_rcu(&client->rcu, evdev_reclaim_client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int evdev_open_device(struct evdev *evdev)
|
|
|
|
@@ -427,7 +437,6 @@ static int evdev_release(struct inode *i
|
|
|
|
{
|
|
|
|
struct evdev_client *client = file->private_data;
|
|
|
|
struct evdev *evdev = client->evdev;
|
|
|
|
- unsigned int i;
|
|
|
|
|
|
|
|
mutex_lock(&evdev->mutex);
|
|
|
|
|
|
|
|
@@ -439,11 +448,6 @@ static int evdev_release(struct inode *i
|
|
|
|
|
|
|
|
evdev_detach_client(evdev, client);
|
|
|
|
|
|
|
|
- for (i = 0; i < EV_CNT; ++i)
|
|
|
|
- bitmap_free(client->evmasks[i]);
|
|
|
|
-
|
|
|
|
- kvfree(client);
|
|
|
|
-
|
|
|
|
evdev_close_device(evdev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
@@ -486,7 +490,6 @@ static int evdev_open(struct inode *inod
|
|
|
|
|
|
|
|
err_free_client:
|
|
|
|
evdev_detach_client(evdev, client);
|
|
|
|
- kvfree(client);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|