* [PATCH 1/3] evdev: convert input_event to input_event64
2015-11-02 13:35 [PATCH 0/3] fix y2038 problem in input_event WEN Pingbo
@ 2015-11-02 13:35 ` WEN Pingbo
2015-11-02 13:35 ` [PATCH 2/3] evdev: add new ioctl EVIOCSEVENT / EVIOCGEVENT WEN Pingbo
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: WEN Pingbo @ 2015-11-02 13:35 UTC (permalink / raw)
To: linux-input; +Cc: y2038, arnd, dmitry.torokhov, WEN Pingbo
struct input_event is not y2038 safe. This patch try to convert it to
input_event64, which replaced timeval with timespec64.
There are many userspace programs, which use input_event to talk with
evdev interface. In order to keep compatible with those binary, we add
a flag (is_input_event64) to indicate which structure is used in current
evdev_client, and do input_event/input_event64 conversion in
input_event_from/to_user().
Userland can get / set is_input_event64 flag via EVIOCGEVENT / EVIOCSEVENT
ioctl command.
According to current situation, we set is_input_event64 to false by
default. So that all old userspace programs will work normally. And
we need another patch to change this option, after most of programs have
moved to new structure.
Signed-off-by: WEN Pingbo <pingbo.wen@linaro.org>
---
drivers/input/evdev.c | 29 ++++++++-------
drivers/input/input-compat.c | 88 +++++++++++++++++++++++++++++++++++++-------
drivers/input/input-compat.h | 9 +++--
include/uapi/linux/input.h | 15 ++++++++
4 files changed, 110 insertions(+), 31 deletions(-)
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 08d4964..815487f 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -58,8 +58,9 @@ struct evdev_client {
struct list_head node;
int clk_type;
bool revoked;
+ bool is_input_event64;
unsigned int bufsize;
- struct input_event buffer[];
+ struct input_event64 *buffer;
};
/* flush queued events of type @type, caller must hold client->buffer_lock */
@@ -68,7 +69,7 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
unsigned int i, head, num;
unsigned int mask = client->bufsize - 1;
bool is_report;
- struct input_event *ev;
+ struct input_event64 *ev;
BUG_ON(type == EV_SYN);
@@ -110,7 +111,7 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
static void __evdev_queue_syn_dropped(struct evdev_client *client)
{
- struct input_event ev;
+ struct input_event64 ev;
ktime_t time;
time = client->clk_type == EV_CLK_REAL ?
@@ -119,7 +120,7 @@ static void __evdev_queue_syn_dropped(struct evdev_client *client)
ktime_get() :
ktime_get_boottime();
- ev.time = ktime_to_timeval(time);
+ ev.time = ktime_to_timespec64(time);
ev.type = EV_SYN;
ev.code = SYN_DROPPED;
ev.value = 0;
@@ -182,7 +183,7 @@ static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
}
static void __pass_event(struct evdev_client *client,
- const struct input_event *event)
+ const struct input_event64 *event)
{
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
@@ -214,13 +215,13 @@ static void evdev_pass_values(struct evdev_client *client,
{
struct evdev *evdev = client->evdev;
const struct input_value *v;
- struct input_event event;
+ struct input_event64 event;
bool wakeup = false;
if (client->revoked)
return;
- event.time = ktime_to_timeval(ev_time[client->clk_type]);
+ event.time = ktime_to_timespec64(ev_time[client->clk_type]);
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
@@ -438,7 +439,7 @@ static int evdev_open(struct inode *inode, struct file *file)
struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
unsigned int size = sizeof(struct evdev_client) +
- bufsize * sizeof(struct input_event);
+ bufsize * sizeof(struct input_event64);
struct evdev_client *client;
int error;
@@ -473,7 +474,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
- struct input_event event;
+ struct input_event64 event;
int retval = 0;
if (count != 0 && count < input_event_size())
@@ -490,7 +491,8 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
while (retval + input_event_size() <= count) {
- if (input_event_from_user(buffer + retval, &event)) {
+ if (input_event_from_user(buffer + retval, &event,
+ client->is_input_event64)) {
retval = -EFAULT;
goto out;
}
@@ -506,7 +508,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
}
static int evdev_fetch_next_event(struct evdev_client *client,
- struct input_event *event)
+ struct input_event64 *event)
{
int have_event;
@@ -528,7 +530,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
- struct input_event event;
+ struct input_event64 event;
size_t read = 0;
int error;
@@ -553,7 +555,8 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
- if (input_event_to_user(buffer + read, &event))
+ if (input_event_to_user(buffer + read, &event,
+ client->is_input_event64))
return -EFAULT;
read += input_event_size();
diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c
index 64ca711..9bad69a 100644
--- a/drivers/input/input-compat.c
+++ b/drivers/input/input-compat.c
@@ -12,10 +12,30 @@
#include <asm/uaccess.h>
#include "input-compat.h"
+void input_event_to_event64(const struct input_event *event,
+ struct input_event64 *event64)
+{
+ event64->time.tv_sec = event->time.tv_sec;
+ event64->time.tv_nsec = event->time.tv_usec * NSEC_PER_USEC;
+ event64->type = event->type;
+ event64->code = event->code;
+ event64->value = event->value;
+}
+
+void input_event64_to_event(const struct input_event64 *event64,
+ struct input_event *event)
+{
+ event->time.tv_sec = event64->time.tv_sec;
+ event->time.tv_usec = event64->time.tv_nsec / NSEC_PER_USEC;
+ event->type = event64->type;
+ event->code = event64->code;
+ event->value = event64->value;
+}
+
#ifdef CONFIG_COMPAT
int input_event_from_user(const char __user *buffer,
- struct input_event *event)
+ struct input_event64 *event, bool is_input_event64)
{
if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event;
@@ -25,27 +45,37 @@ int input_event_from_user(const char __user *buffer,
return -EFAULT;
event->time.tv_sec = compat_event.time.tv_sec;
- event->time.tv_usec = compat_event.time.tv_usec;
+ event->time.tv_nsec = compat_event.time.tv_usec * NSEC_PER_USEC;
event->type = compat_event.type;
event->code = compat_event.code;
event->value = compat_event.value;
} else {
- if (copy_from_user(event, buffer, sizeof(struct input_event)))
- return -EFAULT;
+ if (is_input_event64) {
+ if (copy_from_user(event, buffer,
+ sizeof(struct input_event64)))
+ return -EFAULT;
+ } else { /* userland use struct input_event */
+ struct input_event ev;
+
+ if (copy_from_user(&ev, buffer,
+ sizeof(struct input_event)))
+ return -EFAULT;
+ input_event_to_event64(&ev, event);
+ }
}
return 0;
}
int input_event_to_user(char __user *buffer,
- const struct input_event *event)
+ const struct input_event64 *event, bool is_input_event64)
{
if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event;
compat_event.time.tv_sec = event->time.tv_sec;
- compat_event.time.tv_usec = event->time.tv_usec;
+ compat_event.time.tv_usec = event->time.tv_nsec / NSEC_PER_USEC;
compat_event.type = event->type;
compat_event.code = event->code;
compat_event.value = event->value;
@@ -55,8 +85,18 @@ int input_event_to_user(char __user *buffer,
return -EFAULT;
} else {
- if (copy_to_user(buffer, event, sizeof(struct input_event)))
- return -EFAULT;
+ if (is_input_event64) {
+ if (copy_to_user(buffer, event,
+ sizeof(struct input_event64)))
+ return -EFAULT;
+ } else { /* userland use struct input_event */
+ struct input_event ev;
+
+ input_event64_to_event(event, &ev);
+ if (copy_to_user(buffer, &ev,
+ sizeof(struct input_event)))
+ return -EFAULT;
+ }
}
return 0;
@@ -100,19 +140,39 @@ int input_ff_effect_from_user(const char __user *buffer, size_t size,
#else
int input_event_from_user(const char __user *buffer,
- struct input_event *event)
+ struct input_event64 *event, bool is_input_event64)
{
- if (copy_from_user(event, buffer, sizeof(struct input_event)))
- return -EFAULT;
+ if (is_input_event64) {
+ if (copy_from_user(event, buffer,
+ sizeof(struct input_event64)))
+ return -EFAULT;
+ } else { /* userland use struct input_event */
+ struct input_event ev;
+
+ if (copy_from_user(&ev, buffer,
+ sizeof(struct input_event)))
+ return -EFAULT;
+ input_event_to_event64(&ev, event);
+ }
return 0;
}
int input_event_to_user(char __user *buffer,
- const struct input_event *event)
+ const struct input_event64 *event, bool is_input_event64)
{
- if (copy_to_user(buffer, event, sizeof(struct input_event)))
- return -EFAULT;
+ if (is_input_event64) {
+ if (copy_to_user(buffer, event,
+ sizeof(struct input_event64)))
+ return -EFAULT;
+ } else { /* userland use struct input_event */
+ struct input_event ev;
+
+ input_event64_to_event(event, &ev);
+ if (copy_to_user(buffer, &ev,
+ sizeof(struct input_event)))
+ return -EFAULT;
+ }
return 0;
}
diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h
index 148f66f..f770b5d 100644
--- a/drivers/input/input-compat.h
+++ b/drivers/input/input-compat.h
@@ -68,23 +68,24 @@ struct ff_effect_compat {
static inline size_t input_event_size(void)
{
return (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) ?
- sizeof(struct input_event_compat) : sizeof(struct input_event);
+ sizeof(struct input_event_compat) :
+ sizeof(struct input_event64);
}
#else
static inline size_t input_event_size(void)
{
- return sizeof(struct input_event);
+ return sizeof(struct input_event64);
}
#endif /* CONFIG_COMPAT */
int input_event_from_user(const char __user *buffer,
- struct input_event *event);
+ struct input_event64 *event, bool is_input_event64);
int input_event_to_user(char __user *buffer,
- const struct input_event *event);
+ const struct input_event64 *event, bool is_input_event64);
int input_ff_effect_from_user(const char __user *buffer, size_t size,
struct ff_effect *effect);
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 731417c..1e252ff 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -21,6 +21,14 @@
* The event structure itself
*/
+/*
+ * This structure should not use anymore, please
+ * use input_event64 instead. Placing it here is
+ * only for compatibility purpose.
+ *
+ * FIXME: move to input-compat.h if there are no
+ * compatibility issues.
+ */
struct input_event {
struct timeval time;
__u16 type;
@@ -28,6 +36,13 @@ struct input_event {
__s32 value;
};
+struct input_event64 {
+ struct timespec64 time;
+ __u16 type;
+ __u16 code;
+ __s32 value;
+};
+
/*
* Protocol version.
*/
--
1.9.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/3] uinput: convert input_event to input_event64
2015-11-02 13:35 [PATCH 0/3] fix y2038 problem in input_event WEN Pingbo
2015-11-02 13:35 ` [PATCH 1/3] evdev: convert input_event to input_event64 WEN Pingbo
2015-11-02 13:35 ` [PATCH 2/3] evdev: add new ioctl EVIOCSEVENT / EVIOCGEVENT WEN Pingbo
@ 2015-11-02 13:35 ` WEN Pingbo
2015-11-03 1:43 ` [PATCH 0/3] fix y2038 problem in input_event Dmitry Torokhov
3 siblings, 0 replies; 7+ messages in thread
From: WEN Pingbo @ 2015-11-02 13:35 UTC (permalink / raw)
To: linux-input; +Cc: y2038, arnd, dmitry.torokhov, WEN Pingbo
evdev has converted to input_event64, so is uinput.
Since uinput uses input_event to talk with driver, we hardcode
is_input_event64 flag here directly.
Signed-off-by: WEN Pingbo <pingbo.wen@linaro.org>
---
drivers/input/misc/uinput.c | 17 +++++++++++------
include/linux/uinput.h | 2 +-
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 5adbced..d60dc6f 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -50,7 +50,7 @@ static int uinput_dev_event(struct input_dev *dev,
udev->buff[udev->head].type = type;
udev->buff[udev->head].code = code;
udev->buff[udev->head].value = value;
- do_gettimeofday(&udev->buff[udev->head].time);
+ getnstimeofday64(&udev->buff[udev->head].time);
udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
wake_up_interruptible(&udev->waitq);
@@ -436,7 +436,7 @@ static int uinput_setup_device(struct uinput_device *udev,
static ssize_t uinput_inject_events(struct uinput_device *udev,
const char __user *buffer, size_t count)
{
- struct input_event ev;
+ struct input_event64 ev;
size_t bytes = 0;
if (count != 0 && count < input_event_size())
@@ -448,8 +448,10 @@ static ssize_t uinput_inject_events(struct uinput_device *udev,
* we are still going to return EFAULT instead of partial
* count to let userspace know that it got it's buffers
* all wrong.
+ *
+ * Force set is_input_event64 to false
*/
- if (input_event_from_user(buffer + bytes, &ev))
+ if (input_event_from_user(buffer + bytes, &ev, 0))
return -EFAULT;
input_event(udev->dev, ev.type, ev.code, ev.value);
@@ -482,7 +484,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
}
static bool uinput_fetch_next_event(struct uinput_device *udev,
- struct input_event *event)
+ struct input_event64 *event)
{
bool have_event;
@@ -502,13 +504,16 @@ static bool uinput_fetch_next_event(struct uinput_device *udev,
static ssize_t uinput_events_to_user(struct uinput_device *udev,
char __user *buffer, size_t count)
{
- struct input_event event;
+ struct input_event64 event;
size_t read = 0;
while (read + input_event_size() <= count &&
uinput_fetch_next_event(udev, &event)) {
- if (input_event_to_user(buffer + read, &event))
+ /*
+ * Force set is_input_event64 to false
+ */
+ if (input_event_to_user(buffer + read, &event, 0))
return -EFAULT;
read += input_event_size();
diff --git a/include/linux/uinput.h b/include/linux/uinput.h
index 0994c0d..17859c0 100644
--- a/include/linux/uinput.h
+++ b/include/linux/uinput.h
@@ -66,7 +66,7 @@ struct uinput_device {
unsigned char ready;
unsigned char head;
unsigned char tail;
- struct input_event buff[UINPUT_BUFFER_SIZE];
+ struct input_event64 buff[UINPUT_BUFFER_SIZE];
unsigned int ff_effects_max;
struct uinput_request *requests[UINPUT_NUM_REQUESTS];
--
1.9.1
^ permalink raw reply related [flat|nested] 7+ messages in thread