Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:
 "Some more updates for the input subsystem.

  You will get a fix for race in mousedev that has been causing quite a
  few oopses lately and a small fixup for force feedback support in
  evdev"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: mousedev - fix race when creating mixed device
  Input: don't modify the id of ioctl-provided ff effect on upload failure
This commit is contained in:
Linus Torvalds 2014-03-30 17:20:40 -07:00
commit 915ac4e26e
2 changed files with 45 additions and 32 deletions

View File

@ -954,11 +954,13 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return -EFAULT; return -EFAULT;
error = input_ff_upload(dev, &effect, file); error = input_ff_upload(dev, &effect, file);
if (error)
return error;
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
return -EFAULT; return -EFAULT;
return error; return 0;
} }
/* Multi-number variable-length handlers */ /* Multi-number variable-length handlers */

View File

@ -67,7 +67,6 @@ struct mousedev {
struct device dev; struct device dev;
struct cdev cdev; struct cdev cdev;
bool exist; bool exist;
bool is_mixdev;
struct list_head mixdev_node; struct list_head mixdev_node;
bool opened_by_mixdev; bool opened_by_mixdev;
@ -77,6 +76,9 @@ struct mousedev {
int old_x[4], old_y[4]; int old_x[4], old_y[4];
int frac_dx, frac_dy; int frac_dx, frac_dy;
unsigned long touch; unsigned long touch;
int (*open_device)(struct mousedev *mousedev);
void (*close_device)(struct mousedev *mousedev);
}; };
enum mousedev_emul { enum mousedev_emul {
@ -116,9 +118,6 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
static struct mousedev *mousedev_mix; static struct mousedev *mousedev_mix;
static LIST_HEAD(mousedev_mix_list); static LIST_HEAD(mousedev_mix_list);
static void mixdev_open_devices(void);
static void mixdev_close_devices(void);
#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03]) #define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
@ -428,9 +427,7 @@ static int mousedev_open_device(struct mousedev *mousedev)
if (retval) if (retval)
return retval; return retval;
if (mousedev->is_mixdev) if (!mousedev->exist)
mixdev_open_devices();
else if (!mousedev->exist)
retval = -ENODEV; retval = -ENODEV;
else if (!mousedev->open++) { else if (!mousedev->open++) {
retval = input_open_device(&mousedev->handle); retval = input_open_device(&mousedev->handle);
@ -446,9 +443,7 @@ static void mousedev_close_device(struct mousedev *mousedev)
{ {
mutex_lock(&mousedev->mutex); mutex_lock(&mousedev->mutex);
if (mousedev->is_mixdev) if (mousedev->exist && !--mousedev->open)
mixdev_close_devices();
else if (mousedev->exist && !--mousedev->open)
input_close_device(&mousedev->handle); input_close_device(&mousedev->handle);
mutex_unlock(&mousedev->mutex); mutex_unlock(&mousedev->mutex);
@ -459,21 +454,29 @@ static void mousedev_close_device(struct mousedev *mousedev)
* stream. Note that this function is called with mousedev_mix->mutex * stream. Note that this function is called with mousedev_mix->mutex
* held. * held.
*/ */
static void mixdev_open_devices(void) static int mixdev_open_devices(struct mousedev *mixdev)
{ {
struct mousedev *mousedev; int error;
if (mousedev_mix->open++) error = mutex_lock_interruptible(&mixdev->mutex);
return; if (error)
return error;
list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { if (!mixdev->open++) {
if (!mousedev->opened_by_mixdev) { struct mousedev *mousedev;
if (mousedev_open_device(mousedev))
continue;
mousedev->opened_by_mixdev = true; list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
if (!mousedev->opened_by_mixdev) {
if (mousedev_open_device(mousedev))
continue;
mousedev->opened_by_mixdev = true;
}
} }
} }
mutex_unlock(&mixdev->mutex);
return 0;
} }
/* /*
@ -481,19 +484,22 @@ static void mixdev_open_devices(void)
* device. Note that this function is called with mousedev_mix->mutex * device. Note that this function is called with mousedev_mix->mutex
* held. * held.
*/ */
static void mixdev_close_devices(void) static void mixdev_close_devices(struct mousedev *mixdev)
{ {
struct mousedev *mousedev; mutex_lock(&mixdev->mutex);
if (--mousedev_mix->open) if (!--mixdev->open) {
return; struct mousedev *mousedev;
list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
if (mousedev->opened_by_mixdev) { if (mousedev->opened_by_mixdev) {
mousedev->opened_by_mixdev = false; mousedev->opened_by_mixdev = false;
mousedev_close_device(mousedev); mousedev_close_device(mousedev);
}
} }
} }
mutex_unlock(&mixdev->mutex);
} }
@ -522,7 +528,7 @@ static int mousedev_release(struct inode *inode, struct file *file)
mousedev_detach_client(mousedev, client); mousedev_detach_client(mousedev, client);
kfree(client); kfree(client);
mousedev_close_device(mousedev); mousedev->close_device(mousedev);
return 0; return 0;
} }
@ -550,7 +556,7 @@ static int mousedev_open(struct inode *inode, struct file *file)
client->mousedev = mousedev; client->mousedev = mousedev;
mousedev_attach_client(mousedev, client); mousedev_attach_client(mousedev, client);
error = mousedev_open_device(mousedev); error = mousedev->open_device(mousedev);
if (error) if (error)
goto err_free_client; goto err_free_client;
@ -861,16 +867,21 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
if (mixdev) { if (mixdev) {
dev_set_name(&mousedev->dev, "mice"); dev_set_name(&mousedev->dev, "mice");
mousedev->open_device = mixdev_open_devices;
mousedev->close_device = mixdev_close_devices;
} else { } else {
int dev_no = minor; int dev_no = minor;
/* Normalize device number if it falls into legacy range */ /* Normalize device number if it falls into legacy range */
if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS) if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS)
dev_no -= MOUSEDEV_MINOR_BASE; dev_no -= MOUSEDEV_MINOR_BASE;
dev_set_name(&mousedev->dev, "mouse%d", dev_no); dev_set_name(&mousedev->dev, "mouse%d", dev_no);
mousedev->open_device = mousedev_open_device;
mousedev->close_device = mousedev_close_device;
} }
mousedev->exist = true; mousedev->exist = true;
mousedev->is_mixdev = mixdev;
mousedev->handle.dev = input_get_device(dev); mousedev->handle.dev = input_get_device(dev);
mousedev->handle.name = dev_name(&mousedev->dev); mousedev->handle.name = dev_name(&mousedev->dev);
mousedev->handle.handler = handler; mousedev->handle.handler = handler;
@ -919,7 +930,7 @@ static void mousedev_destroy(struct mousedev *mousedev)
device_del(&mousedev->dev); device_del(&mousedev->dev);
mousedev_cleanup(mousedev); mousedev_cleanup(mousedev);
input_free_minor(MINOR(mousedev->dev.devt)); input_free_minor(MINOR(mousedev->dev.devt));
if (!mousedev->is_mixdev) if (mousedev != mousedev_mix)
input_unregister_handle(&mousedev->handle); input_unregister_handle(&mousedev->handle);
put_device(&mousedev->dev); put_device(&mousedev->dev);
} }