Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F162923568
D15070.id48372.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D15070.id48372.diff
View Options
Index: sys/dev/atkbdc/atkbd.c
===================================================================
--- sys/dev/atkbdc/atkbd.c
+++ sys/dev/atkbdc/atkbd.c
@@ -475,6 +475,7 @@
evdev_set_id(evdev, BUS_I8042, PS2_KEYBOARD_VENDOR,
PS2_KEYBOARD_PRODUCT, 0);
evdev_set_methods(evdev, kbd, &atkbd_evdev_methods);
+ evdev_set_flag(evdev, EVDEV_FLAG_LOCKLESS);
evdev_support_event(evdev, EV_SYN);
evdev_support_event(evdev, EV_KEY);
evdev_support_event(evdev, EV_LED);
@@ -484,7 +485,7 @@
evdev_support_led(evdev, LED_CAPSL);
evdev_support_led(evdev, LED_SCROLLL);
- if (evdev_register(evdev))
+ if (evdev_register_mtx(evdev, &Giant))
evdev_free(evdev);
else
state->ks_evdev = evdev;
Index: sys/dev/evdev/cdev.c
===================================================================
--- sys/dev/evdev/cdev.c
+++ sys/dev/evdev/cdev.c
@@ -110,8 +110,12 @@
client->ec_buffer_ready = 0;
client->ec_evdev = evdev;
- mtx_init(&client->ec_buffer_mtx, "evclient", "evdev", MTX_DEF);
- knlist_init_mtx(&client->ec_selp.si_note, &client->ec_buffer_mtx);
+ if (!bit_test(evdev->ev_flags, EVDEV_FLAG_LOCKLESS)) {
+ mtx_init(&client->ec_buffer_mtx, "evclient", "evdev", MTX_DEF);
+ knlist_init_mtx(&client->ec_selp.si_note,
+ &client->ec_buffer_mtx);
+ } else
+ knlist_init_mtx(&client->ec_selp.si_note, evdev->ev_lock);
/* Avoid race with evdev_unregister */
EVDEV_LOCK(evdev);
@@ -149,11 +153,19 @@
evdev_dispose_client(client->ec_evdev, client);
EVDEV_UNLOCK(client->ec_evdev);
- knlist_clear(&client->ec_selp.si_note, 0);
+ /*
+ * Take kqueue lock explicitly. Otherwise lockless mode will trigger
+ * knlist_cleardel() 'Mutex owned' assertion as devfs can call
+ * evdev_dtor() with Giant lock already taken.
+ */
+ EVDEV_CLIENT_LOCKQ(client);
+ knlist_clear(&client->ec_selp.si_note, 1);
+ EVDEV_CLIENT_UNLOCKQ(client);
seldrain(&client->ec_selp);
knlist_destroy(&client->ec_selp.si_note);
funsetown(&client->ec_sigio);
- mtx_destroy(&client->ec_buffer_mtx);
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client))
+ mtx_destroy(&client->ec_buffer_mtx);
free(client, M_EVDEV);
}
@@ -202,9 +214,11 @@
(client->ec_buffer_head + 1) % client->ec_buffer_size;
remaining--;
- EVDEV_CLIENT_UNLOCKQ(client);
+ if (!EVDEV_CLIENT_LOCKQ_SLEEPABLE(client))
+ EVDEV_CLIENT_UNLOCKQ(client);
ret = uiomove(&event, sizeof(struct input_event), uio);
- EVDEV_CLIENT_LOCKQ(client);
+ if (!EVDEV_CLIENT_LOCKQ_SLEEPABLE(client))
+ EVDEV_CLIENT_LOCKQ(client);
}
EVDEV_CLIENT_UNLOCKQ(client);
@@ -349,6 +363,19 @@
if (client->ec_revoked || evdev == NULL)
return (ENODEV);
+ /*
+ * Fix evdev state corrupted with discarding of kdb events.
+ * EVIOCGKEY and EVIOCGLED ioctls can suffer from this.
+ */
+ if (evdev->ev_kdb_active) {
+ EVDEV_LOCK(evdev);
+ if (evdev->ev_kdb_active) {
+ evdev->ev_kdb_active = false;
+ evdev_restore_after_kdb(evdev);
+ }
+ EVDEV_UNLOCK(evdev);
+ }
+
/* file I/O ioctl handling */
switch (cmd) {
case FIOSETOWN:
@@ -820,7 +847,8 @@
size_t head, tail, count, i;
bool last_was_syn = false;
- EVDEV_CLIENT_LOCKQ(client);
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client))
+ EVDEV_CLIENT_LOCKQ(client);
i = head = client->ec_buffer_head;
tail = client->ec_buffer_tail;
@@ -856,5 +884,6 @@
client->ec_buffer_head = i;
client->ec_buffer_tail = tail;
- EVDEV_CLIENT_UNLOCKQ(client);
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client))
+ EVDEV_CLIENT_UNLOCKQ(client);
}
Index: sys/dev/evdev/evdev.h
===================================================================
--- sys/dev/evdev/evdev.h
+++ sys/dev/evdev/evdev.h
@@ -87,6 +87,7 @@
* for MT protocol type B reports */
#define EVDEV_FLAG_MT_AUTOREL 0x02 /* Autorelease MT-slots not listed in
* current MT protocol type B report */
+#define EVDEV_FLAG_LOCKLESS 0x03 /* Avoid locks in evdev_push() */
#define EVDEV_FLAG_MAX 0x1F
#define EVDEV_FLAG_CNT (EVDEV_FLAG_MAX + 1)
Index: sys/dev/evdev/evdev.c
===================================================================
--- sys/dev/evdev/evdev.c
+++ sys/dev/evdev/evdev.c
@@ -27,11 +27,13 @@
* $FreeBSD$
*/
+#include "opt_ddb.h"
#include "opt_evdev.h"
#include <sys/param.h>
#include <sys/bitstring.h>
#include <sys/conf.h>
+#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
@@ -64,8 +66,15 @@
MALLOC_DEFINE(M_EVDEV, "evdev", "evdev memory");
+#ifdef DDB
+#define EVDEV_HZ_DEFAULT 20
+#else
+#define EVDEV_HZ_DEFAULT 0
+#endif
+
int evdev_rcpt_mask = EVDEV_RCPT_SYSMOUSE | EVDEV_RCPT_KBDMUX;
int evdev_sysmouse_t_axis = 0;
+static int evdev_hz = EVDEV_HZ_DEFAULT;
#ifdef EVDEV_SUPPORT
SYSCTL_NODE(_kern, OID_AUTO, evdev, CTLFLAG_RW, 0, "Evdev args");
@@ -74,10 +83,15 @@
"bit2 - mouse hardware, bit3 - keyboard hardware");
SYSCTL_INT(_kern_evdev, OID_AUTO, sysmouse_t_axis, CTLFLAG_RW,
&evdev_sysmouse_t_axis, 0, "Extract T-axis from 0-none, 1-ums, 2-psm");
+/* Setting to 0 disables lockless mode and enables notification on interrupt */
+SYSCTL_INT(_kern_evdev, OID_AUTO, poll_rate, CTLFLAG_RW,
+ &evdev_hz, 0, "Userland notification rate (hz), 0 - disable polling");
#endif
static void evdev_start_repeat(struct evdev_dev *, uint16_t);
static void evdev_stop_repeat(struct evdev_dev *);
+static void evdev_start_notification(struct evdev_dev *);
+static void evdev_stop_notification(struct evdev_dev *);
static int evdev_check_event(struct evdev_dev *, uint16_t, uint16_t, int32_t);
static inline void
@@ -207,8 +221,6 @@
if (evdev_event_supported(evdev, EV_REP) &&
bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT)) {
- /* Initialize callout */
- callout_init_mtx(&evdev->ev_rep_callout, &evdev->ev_mtx, 0);
if (evdev->ev_rep[REP_DELAY] == 0 &&
evdev->ev_rep[REP_PERIOD] == 0) {
@@ -217,6 +229,8 @@
evdev->ev_rep[REP_PERIOD] = 33;
}
}
+ callout_init_mtx(&evdev->ev_rep_callout, evdev->ev_lock, 0);
+ callout_init_mtx(&evdev->ev_note_callout, evdev->ev_lock, 0);
/* Initialize multitouch protocol type B states */
if (bit_test(evdev->ev_abs_flags, ABS_MT_SLOT) &&
@@ -276,9 +290,11 @@
LIST_FOREACH(client, &evdev->ev_clients, ec_link) {
evdev_revoke_client(client);
evdev_dispose_client(evdev, client);
- EVDEV_CLIENT_LOCKQ(client);
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client))
+ EVDEV_CLIENT_LOCKQ(client);
evdev_notify_event(client);
- EVDEV_CLIENT_UNLOCKQ(client);
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client))
+ EVDEV_CLIENT_UNLOCKQ(client);
}
EVDEV_UNLOCK(evdev);
@@ -730,12 +746,19 @@
if (evdev->ev_grabber != NULL && evdev->ev_grabber != client)
continue;
- EVDEV_CLIENT_LOCKQ(client);
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client))
+ EVDEV_CLIENT_LOCKQ(client);
evdev_client_push(client, type, code, value);
- if (type == EV_SYN && code == SYN_REPORT)
+ /* wakeup, selwakeup and knote take internal locks */
+ if (type == EV_SYN && code == SYN_REPORT && !(evdev_hz > 0 &&
+ bit_test(evdev->ev_flags, EVDEV_FLAG_LOCKLESS)))
evdev_notify_event(client);
- EVDEV_CLIENT_UNLOCKQ(client);
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client))
+ EVDEV_CLIENT_UNLOCKQ(client);
}
+ if (type == EV_SYN && code == SYN_REPORT && evdev_hz > 0 &&
+ bit_test(evdev->ev_flags, EVDEV_FLAG_LOCKLESS))
+ evdev->ev_note = true;
evdev->ev_event_count++;
}
@@ -763,6 +786,30 @@
}
}
+void
+evdev_restore_after_kdb(struct evdev_dev *evdev)
+{
+ int code;
+
+ EVDEV_LOCK_ASSERT(evdev);
+
+ /* Report postponed leds */
+ for (code = 0; code < LED_CNT; code++)
+ if (bit_test(evdev->ev_kdb_led_states, code))
+ evdev_send_event(evdev, EV_LED, code,
+ !bit_test(evdev->ev_led_states, code));
+ bit_nclear(evdev->ev_kdb_led_states, 0, LED_MAX);
+
+ /* Release stuck keys (CTRL + ALT + ESC) */
+ evdev_stop_repeat(evdev);
+ for (code = 0; code < KEY_CNT; code++) {
+ if (bit_test(evdev->ev_key_states, code)) {
+ evdev_send_event(evdev, EV_KEY, code, KEY_EVENT_UP);
+ evdev_send_event(evdev, EV_SYN, SYN_REPORT, 1);
+ }
+ }
+}
+
int
evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
int32_t value)
@@ -771,8 +818,29 @@
if (evdev_check_event(evdev, type, code, value) != 0)
return (EINVAL);
+ /*
+ * Discard all but LEDs kdb events as unrelated to userspace.
+ * Aggregate LED updates and postpone reporting until kdb deactivation.
+ */
+ if (kdb_active) {
+ evdev->ev_kdb_active = true;
+ if (type == EV_LED)
+ bit_set(evdev->ev_kdb_led_states,
+ bit_test(evdev->ev_led_states, code) != value);
+ return (0);
+ }
+
EVDEV_ENTER(evdev);
+ /*
+ * Fix evdev state corrupted with discarding of kdb events.
+ * Usually fires on ENTER key release after 'continue' have been typed.
+ */
+ if (evdev->ev_kdb_active) {
+ evdev->ev_kdb_active = false;
+ evdev_restore_after_kdb(evdev);
+ }
+
evdev_modify_event(evdev, type, code, &value);
if (type == EV_SYN && code == SYN_REPORT &&
bit_test(evdev->ev_flags, EVDEV_FLAG_MT_AUTOREL))
@@ -844,11 +912,14 @@
EVDEV_LOCK_ASSERT(evdev);
- if (LIST_EMPTY(&evdev->ev_clients) && evdev->ev_methods != NULL &&
- evdev->ev_methods->ev_open != NULL) {
+ if (LIST_EMPTY(&evdev->ev_clients)) {
debugf(evdev, "calling ev_open() on device %s",
evdev->ev_shortname);
- ret = evdev->ev_methods->ev_open(evdev);
+ if (evdev->ev_methods != NULL &&
+ evdev->ev_methods->ev_open != NULL)
+ ret = evdev->ev_methods->ev_open(evdev);
+ if (!ret && bit_test(evdev->ev_flags, EVDEV_FLAG_LOCKLESS))
+ evdev_start_notification(evdev);
}
if (ret == 0)
LIST_INSERT_HEAD(&evdev->ev_clients, client, ec_link);
@@ -870,6 +941,8 @@
if (evdev_event_supported(evdev, EV_REP) &&
bit_test(evdev->ev_flags, EVDEV_FLAG_SOFTREPEAT))
evdev_stop_repeat(evdev);
+ if (bit_test(evdev->ev_flags, EVDEV_FLAG_LOCKLESS))
+ evdev_stop_notification(evdev);
}
evdev_release_client(evdev, client);
}
@@ -944,4 +1017,45 @@
}
}
+static void
+evdev_notification_callout(void *arg)
+{
+ struct evdev_dev *evdev = arg;
+ struct evdev_client *client;
+
+ EVDEV_LOCK_ASSERT(evdev);
+
+ if (evdev->ev_note) {
+ evdev->ev_note = false;
+ LIST_FOREACH(client, &evdev->ev_clients, ec_link) {
+ if (evdev->ev_grabber == NULL ||
+ evdev->ev_grabber == client)
+ evdev_notify_event(client);
+ }
+ }
+
+ evdev_start_notification(evdev);
+}
+
+static void
+evdev_start_notification(struct evdev_dev *evdev)
+{
+
+ EVDEV_LOCK_ASSERT(evdev);
+
+ if (evdev_hz > 0)
+ callout_reset(&evdev->ev_note_callout,
+ evdev_hz > hz ? 1 : hz / evdev_hz,
+ evdev_notification_callout, evdev);
+}
+
+static void
+evdev_stop_notification(struct evdev_dev *evdev)
+{
+
+ EVDEV_LOCK_ASSERT(evdev);
+
+ callout_stop(&evdev->ev_note_callout);
+}
+
MODULE_VERSION(evdev, 1);
Index: sys/dev/evdev/evdev_private.h
===================================================================
--- sys/dev/evdev/evdev_private.h
+++ sys/dev/evdev/evdev_private.h
@@ -92,6 +92,10 @@
struct evdev_client * ev_grabber;
size_t ev_report_size;
+ /* Lockless mode: */
+ bool ev_note;
+ struct callout ev_note_callout;
+
/* Supported features: */
bitstr_t bit_decl(ev_prop_flags, INPUT_PROP_CNT);
bitstr_t bit_decl(ev_type_flags, EV_CNT);
@@ -117,6 +121,10 @@
bitstr_t bit_decl(ev_sw_states, SW_CNT);
bool ev_report_opened;
+ /* KDB state: */
+ bool ev_kdb_active;
+ bitstr_t bit_decl(ev_kdb_led_states, LED_CNT);
+
/* Multitouch protocol type B state: */
struct evdev_mt * ev_mt;
@@ -167,15 +175,34 @@
struct input_event ec_buffer[];
};
-#define EVDEV_CLIENT_LOCKQ(client) mtx_lock(&(client)->ec_buffer_mtx)
-#define EVDEV_CLIENT_UNLOCKQ(client) mtx_unlock(&(client)->ec_buffer_mtx)
-#define EVDEV_CLIENT_LOCKQ_ASSERT(client) \
- mtx_assert(&(client)->ec_buffer_mtx, MA_OWNED)
-#define EVDEV_CLIENT_EMPTYQ(client) \
- ((client)->ec_buffer_head == (client)->ec_buffer_ready)
-#define EVDEV_CLIENT_SIZEQ(client) \
- (((client)->ec_buffer_ready + (client)->ec_buffer_size - \
- (client)->ec_buffer_head) % (client)->ec_buffer_size)
+#define EVDEV_CLIENT_LOCKQ_EXISTS(client) \
+ mtx_initialized(&(client)->ec_buffer_mtx)
+#define EVDEV_CLIENT_LOCKQ(client) do { \
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client)) \
+ mtx_lock(&(client)->ec_buffer_mtx); \
+ else \
+ EVDEV_LOCK((client)->ec_evdev); \
+} while (0)
+#define EVDEV_CLIENT_UNLOCKQ(client) do { \
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client)) \
+ mtx_unlock(&(client)->ec_buffer_mtx); \
+ else \
+ EVDEV_UNLOCK((client)->ec_evdev); \
+} while (0)
+#define EVDEV_CLIENT_LOCKQ_ASSERT(client) do { \
+ if (EVDEV_CLIENT_LOCKQ_EXISTS(client)) \
+ mtx_assert(&(client)->ec_buffer_mtx, MA_OWNED); \
+ else \
+ EVDEV_LOCK_ASSERT((client)->ec_evdev); \
+} while (0)
+#define EVDEV_CLIENT_LOCKQ_SLEEPABLE(client) \
+ (!EVDEV_CLIENT_LOCKQ_EXISTS(client) && \
+ (client)->ec_evdev->ev_lock == &Giant)
+#define EVDEV_CLIENT_EMPTYQ(client) \
+ ((client)->ec_buffer_head == (client)->ec_buffer_ready)
+#define EVDEV_CLIENT_SIZEQ(client) \
+ (((client)->ec_buffer_ready + (client)->ec_buffer_size - \
+ (client)->ec_buffer_head) % (client)->ec_buffer_size)
/* Input device interface: */
void evdev_send_event(struct evdev_dev *, uint16_t, uint16_t, int32_t);
@@ -185,6 +212,7 @@
bool evdev_event_supported(struct evdev_dev *, uint16_t);
void evdev_set_abs_bit(struct evdev_dev *, uint16_t);
void evdev_set_absinfo(struct evdev_dev *, uint16_t, struct input_absinfo *);
+void evdev_restore_after_kdb(struct evdev_dev *);
/* Client interface: */
int evdev_register_client(struct evdev_dev *, struct evdev_client *);
Index: sys/dev/kbdmux/kbdmux.c
===================================================================
--- sys/dev/kbdmux/kbdmux.c
+++ sys/dev/kbdmux/kbdmux.c
@@ -496,6 +496,7 @@
evdev_set_phys(evdev, phys_loc);
evdev_set_id(evdev, BUS_VIRTUAL, 0, 0, 0);
evdev_set_methods(evdev, kbd, &kbdmux_evdev_methods);
+ evdev_set_flag(evdev, EVDEV_FLAG_LOCKLESS);
evdev_support_event(evdev, EV_SYN);
evdev_support_event(evdev, EV_KEY);
evdev_support_event(evdev, EV_LED);
@@ -505,7 +506,7 @@
evdev_support_led(evdev, LED_CAPSL);
evdev_support_led(evdev, LED_SCROLLL);
- if (evdev_register(evdev))
+ if (evdev_register_mtx(evdev, &Giant))
evdev_free(evdev);
else
state->ks_evdev = evdev;
Index: sys/dev/usb/input/ukbd.c
===================================================================
--- sys/dev/usb/input/ukbd.c
+++ sys/dev/usb/input/ukbd.c
@@ -1345,6 +1345,7 @@
uaa->info.idProduct, 0);
evdev_set_serial(evdev, usb_get_serial(uaa->device));
evdev_set_methods(evdev, kbd, &ukbd_evdev_methods);
+ evdev_set_flag(evdev, EVDEV_FLAG_LOCKLESS);
evdev_support_event(evdev, EV_SYN);
evdev_support_event(evdev, EV_KEY);
if (sc->sc_flags & (UKBD_FLAG_NUMLOCK | UKBD_FLAG_CAPSLOCK |
@@ -1361,7 +1362,7 @@
if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK)
evdev_support_led(evdev, LED_SCROLLL);
- if (evdev_register(evdev))
+ if (evdev_register_mtx(evdev, &Giant))
evdev_free(evdev);
else
sc->sc_evdev = evdev;
Index: sys/modules/evdev/Makefile
===================================================================
--- sys/modules/evdev/Makefile
+++ sys/modules/evdev/Makefile
@@ -4,6 +4,6 @@
KMOD= evdev
SRCS= cdev.c evdev.c evdev_mt.c evdev_utils.c
-SRCS+= opt_evdev.h bus_if.h device_if.h
+SRCS+= opt_ddb.h opt_evdev.h bus_if.h device_if.h
.include <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Jul 19, 8:33 AM (9 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35227936
Default Alt Text
D15070.id48372.diff (15 KB)
Attached To
Mode
D15070: evdev - special lock-less mode for keyboard drivers
Attached
Detach File
Event Timeline
Log In to Comment