evdev: disable evdev if it is invoked from KDB or panic context
This allow to prevent deadlock on entering KDB if one of evdev locks is already taken by userspace process. Also this change discards all but LED console events produced by KDB as unrelated to userspace. Tested by: dumbbell (as part of D15070) Objected by: bde (as 'KDB lock an already locked mutex' problem solution) MFC after: 1 month
This commit is contained in:
parent
b9d1ddaf9f
commit
5a823e3c12
@ -349,6 +349,19 @@ evdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
|
||||
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:
|
||||
|
@ -32,9 +32,11 @@
|
||||
#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>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
@ -763,6 +765,30 @@ evdev_send_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
|
||||
}
|
||||
}
|
||||
|
||||
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 +797,26 @@ evdev_push_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
|
||||
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 || SCHEDULER_STOPPED()) {
|
||||
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 */
|
||||
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))
|
||||
|
@ -117,6 +117,10 @@ struct evdev_dev
|
||||
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;
|
||||
|
||||
@ -190,6 +194,7 @@ int evdev_cdev_destroy(struct evdev_dev *);
|
||||
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 *);
|
||||
|
Loading…
Reference in New Issue
Block a user