Page MenuHomeFreeBSD

D15070.id48372.diff
No OneTemporary

D15070.id48372.diff

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

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)

Event Timeline