On Wednesday 28 February 2007 07:45, Rafael J. Wysocki wrote: > > > This gives: > > > > (gdb) l *evdev_disconnect+0xb1 > > 0xa81 is in evdev_disconnect (include/asm/processor.h:716). > > 711 However we don't do prefetches for pre XP Athlons currently > > 712 That should be fixed. */ > > 713 #define ARCH_HAS_PREFETCH > > 714 static inline void prefetch(const void *x) > > 715 { > > 716 alternative_input(ASM_NOP4, > > 717 "prefetchnta (%1)", > > 718 X86_FEATURE_XMM, > > 719 "r" (x)); > > 720 } > > Hm, interesting. Looks like a pointer points to nowhere in > input_unregister_device(), but I don't know which one. This may be > an evdev problem ... >
Please try the patch below. -- Dmitry Input: use krefs for refcounting in input handlers This should fix problems whith accessing memory already freed by another thread. Signed-off-by: Dmitry Torokhov <[EMAIL PROTECTED]> --- drivers/input/evdev.c | 62 ++++++++++------- drivers/input/joydev.c | 51 +++++++++----- drivers/input/mousedev.c | 166 +++++++++++++++++++++++++++++++++-------------- drivers/input/tsdev.c | 65 +++++++++++------- 4 files changed, 227 insertions(+), 117 deletions(-) Index: work/drivers/input/evdev.c =================================================================== --- work.orig/drivers/input/evdev.c +++ work/drivers/input/evdev.c @@ -29,8 +29,10 @@ struct evdev { char name[16]; struct input_handle handle; wait_queue_head_t wait; - struct evdev_list *grab; + struct kref kref; struct list_head list; + + struct evdev_list *grab; }; struct evdev_list { @@ -94,53 +96,62 @@ static int evdev_flush(struct file *file return input_flush_device(&list->evdev->handle, file); } -static void evdev_free(struct evdev *evdev) +static void evdev_free(struct kref *kref) { + struct evdev *evdev = container_of(kref, struct evdev, kref); + evdev_table[evdev->minor] = NULL; kfree(evdev); } -static int evdev_release(struct inode * inode, struct file * file) +static int evdev_release(struct inode *inode, struct file *file) { struct evdev_list *list = file->private_data; + struct evdev *evdev = list->evdev; - if (list->evdev->grab == list) { - input_release_device(&list->evdev->handle); - list->evdev->grab = NULL; + if (evdev->grab == list) { + input_release_device(&evdev->handle); + evdev->grab = NULL; } evdev_fasync(-1, file, 0); + list_del(&list->node); + kfree(list); - if (!--list->evdev->open) { - if (list->evdev->exist) - input_close_device(&list->evdev->handle); - else - evdev_free(list->evdev); - } + if (!--evdev->open && evdev->exist) + input_close_device(&evdev->handle); + + kref_put(&evdev->kref, evdev_free); - kfree(list); return 0; } -static int evdev_open(struct inode * inode, struct file * file) +static int evdev_open(struct inode *inode, struct file *file) { struct evdev_list *list; + struct evdev *evdev; int i = iminor(inode) - EVDEV_MINOR_BASE; - if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist) + if (i >= EVDEV_MINORS) + return -ENODEV; + + evdev = evdev_table[i]; + if (!evdev || !evdev->exist) return -ENODEV; - if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL))) + list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL); + if (!list) return -ENOMEM; - list->evdev = evdev_table[i]; - list_add_tail(&list->node, &evdev_table[i]->list); + kref_get(&evdev->kref); + + list->evdev = evdev; + list_add_tail(&list->node, &evdev->list); file->private_data = list; - if (!list->evdev->open++) - if (list->evdev->exist) - input_open_device(&list->evdev->handle); + if (!evdev->open++ && evdev->exist) + input_open_device(&evdev->handle); return 0; } @@ -629,9 +640,11 @@ static struct input_handle *evdev_connec return NULL; } - if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL))) + evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); + if (!evdev) return NULL; + kref_init(&evdev->kref); INIT_LIST_HEAD(&evdev->list); init_waitqueue_head(&evdev->wait); @@ -672,8 +685,9 @@ static void evdev_disconnect(struct inpu wake_up_interruptible(&evdev->wait); list_for_each_entry(list, &evdev->list, node) kill_fasync(&list->fasync, SIGIO, POLL_HUP); - } else - evdev_free(evdev); + } + + kref_put(&evdev->kref, evdev_free); } static const struct input_device_id evdev_ids[] = { Index: work/drivers/input/joydev.c =================================================================== --- work.orig/drivers/input/joydev.c +++ work/drivers/input/joydev.c @@ -43,7 +43,9 @@ struct joydev { char name[16]; struct input_handle handle; wait_queue_head_t wait; + struct kref kref; struct list_head list; + struct js_corr corr[ABS_MAX + 1]; struct JS_DATA_SAVE_TYPE glue; int nabs; @@ -139,8 +141,10 @@ static int joydev_fasync(int fd, struct return retval < 0 ? retval : 0; } -static void joydev_free(struct joydev *joydev) +static void joydev_free(struct kref *kref) { + struct joydev *joydev = container_of(kref, struct joydev, kref); + joydev_table[joydev->minor] = NULL; kfree(joydev); } @@ -148,40 +152,46 @@ static void joydev_free(struct joydev *j static int joydev_release(struct inode * inode, struct file * file) { struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; joydev_fasync(-1, file, 0); list_del(&list->node); + kfree(list); - if (!--list->joydev->open) { - if (list->joydev->exist) - input_close_device(&list->joydev->handle); - else - joydev_free(list->joydev); - } + if (!--joydev->open && joydev->exist) + input_close_device(&list->joydev->handle); + + kref_put(&joydev->kref, joydev_free); - kfree(list); return 0; } static int joydev_open(struct inode *inode, struct file *file) { struct joydev_list *list; + struct joydev *joydev; int i = iminor(inode) - JOYDEV_MINOR_BASE; - if (i >= JOYDEV_MINORS || !joydev_table[i]) + if (i >= JOYDEV_MINORS) return -ENODEV; - if (!(list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL))) + joydev = joydev_table[i]; + if (!joydev || !joydev->exist) + return -ENODEV; + + list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL); + if (!list) return -ENOMEM; - list->joydev = joydev_table[i]; - list_add_tail(&list->node, &joydev_table[i]->list); + kref_get(&joydev->kref); + + list->joydev = joydev; + list_add_tail(&list->node, &joydev->list); file->private_data = list; - if (!list->joydev->open++) - if (list->joydev->exist) - input_open_device(&list->joydev->handle); + if (!joydev->open++ && joydev->exist) + input_open_device(&list->joydev->handle); return 0; } @@ -198,7 +208,7 @@ static ssize_t joydev_read(struct file * struct input_dev *input = joydev->handle.dev; int retval = 0; - if (!list->joydev->exist) + if (!joydev->exist) return -ENODEV; if (count < sizeof(struct js_event)) @@ -478,9 +488,11 @@ static struct input_handle *joydev_conne return NULL; } - if (!(joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL))) + joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL); + if (!joydev) return NULL; + kref_init(&joydev->kref); INIT_LIST_HEAD(&joydev->list); init_waitqueue_head(&joydev->wait); @@ -559,8 +571,9 @@ static void joydev_disconnect(struct inp wake_up_interruptible(&joydev->wait); list_for_each_entry(list, &joydev->list, node) kill_fasync(&list->fasync, SIGIO, POLL_HUP); - } else - joydev_free(joydev); + } + + kref_put(&joydev->kref, joydev_free); } static const struct input_device_id joydev_blacklist[] = { Index: work/drivers/input/mousedev.c =================================================================== --- work.orig/drivers/input/mousedev.c +++ work/drivers/input/mousedev.c @@ -62,9 +62,13 @@ struct mousedev { int open; int minor; char name[16]; + struct input_handle handle; wait_queue_head_t wait; + struct kref kref; struct list_head list; - struct input_handle handle; + + struct list_head mixdev_node; + int mixdev_open; struct mousedev_hw_data packet; unsigned int pkt_count; @@ -111,6 +115,8 @@ static struct input_handler mousedev_han static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; static struct mousedev mousedev_mix; +static LIST_HEAD(mousedev_mix_list); +static DEFINE_MUTEX(mousedev_mix_mutex); #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) #define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03]) @@ -358,55 +364,112 @@ static int mousedev_fasync(int fd, struc return retval < 0 ? retval : 0; } -static void mousedev_free(struct mousedev *mousedev) +static void mousedev_free(struct kref *kref) { + struct mousedev *mousedev = container_of(kref, struct mousedev, kref); + mousedev_table[mousedev->minor] = NULL; kfree(mousedev); } -static void mixdev_release(void) +static int mixdev_add_device(struct mousedev *mousedev) +{ + int retval = 0; + + mutex_lock(&mousedev_mix_mutex); + + if (mousedev_mix.open) { + if (!mousedev->open && mousedev->exist) { + retval = input_open_device(&mousedev->handle); + if (retval) + goto out; + + mousedev->open++; + } + mousedev->mixdev_open++; + } + + list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); + + out: + mutex_unlock(&mousedev_mix_mutex); + return retval; +} + +static void mixdev_remove_device(struct mousedev *mousedev) { - struct input_handle *handle; + mutex_lock(&mousedev_mix_mutex); - list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { - struct mousedev *mousedev = handle->private; + if (!--mousedev->mixdev_open) + if (!--mousedev->open && mousedev->exist) + input_close_device(&mousedev->handle); - if (!mousedev->open) { - if (mousedev->exist) + list_del_init(&mousedev->mixdev_node); + + mutex_unlock(&mousedev_mix_mutex); +} + +static void mixdev_open_devices(void) +{ + struct mousedev *mousedev; + + mutex_lock(&mousedev_mix_mutex); + + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { + if (!mousedev->mixdev_open) + if (!mousedev->open && mousedev->exist) { + if (input_open_device(&mousedev->handle)) + continue; + mousedev->open++; + } + mousedev->mixdev_open++; + } + + mutex_unlock(&mousedev_mix_mutex); +} + +static void mixdev_close_devices(void) +{ + struct mousedev *mousedev, *next; + + mutex_lock(&mousedev_mix_mutex); + + list_for_each_entry_safe(mousedev, next, &mousedev_mix_list, mixdev_node) { + if (mousedev->mixdev_open) { + if (!--mousedev->open && mousedev->exist) input_close_device(&mousedev->handle); - else - mousedev_free(mousedev); + mousedev->mixdev_open--; } } + + mutex_unlock(&mousedev_mix_mutex); } -static int mousedev_release(struct inode * inode, struct file * file) +static int mousedev_release(struct inode *inode, struct file *file) { struct mousedev_list *list = file->private_data; + struct mousedev *mousedev = list->mousedev; mousedev_fasync(-1, file, 0); list_del(&list->node); + kfree(list); - if (!--list->mousedev->open) { - if (list->mousedev->minor == MOUSEDEV_MIX) - mixdev_release(); - else if (!mousedev_mix.open) { - if (list->mousedev->exist) - input_close_device(&list->mousedev->handle); - else - mousedev_free(list->mousedev); - } + if (!--mousedev->open) { + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_close_devices(); + else if (mousedev->exist) + input_close_device(&mousedev->handle); } - kfree(list); + kref_put(&mousedev->kref, mousedev_free); + return 0; } -static int mousedev_open(struct inode * inode, struct file * file) +static int mousedev_open(struct inode *inode, struct file *file) { struct mousedev_list *list; - struct input_handle *handle; struct mousedev *mousedev; int i; @@ -417,29 +480,31 @@ static int mousedev_open(struct inode * #endif i = iminor(inode) - MOUSEDEV_MINOR_BASE; - if (i >= MOUSEDEV_MINORS || !mousedev_table[i]) + if (i >= MOUSEDEV_MINORS) + return -ENODEV; + + mousedev = mousedev_table[i]; + if (!mousedev || !mousedev->exist) return -ENODEV; - if (!(list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL))) + list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL); + if (!list) return -ENOMEM; + kref_get(&mousedev->kref); + spin_lock_init(&list->packet_lock); list->pos_x = xres / 2; list->pos_y = yres / 2; - list->mousedev = mousedev_table[i]; - list_add_tail(&list->node, &mousedev_table[i]->list); + list->mousedev = mousedev; + list_add_tail(&list->node, &mousedev->list); file->private_data = list; - if (!list->mousedev->open++) { - if (list->mousedev->minor == MOUSEDEV_MIX) { - list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { - mousedev = handle->private; - if (!mousedev->open && mousedev->exist) - input_open_device(handle); - } - } else - if (!mousedev_mix.open && list->mousedev->exist) - input_open_device(&list->mousedev->handle); + if (!mousedev->open++) { + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_open_devices(); + else if (mousedev->exist) + input_open_device(&mousedev->handle); } return 0; @@ -629,7 +694,7 @@ static struct input_handle *mousedev_con { struct mousedev *mousedev; struct class_device *cdev; - int minor = 0; + int minor; for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); if (minor == MOUSEDEV_MINORS) { @@ -637,10 +702,13 @@ static struct input_handle *mousedev_con return NULL; } - if (!(mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL))) + mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); + if (!mousedev) return NULL; + kref_init(&mousedev->kref); INIT_LIST_HEAD(&mousedev->list); + INIT_LIST_HEAD(&mousedev->mixdev_node); init_waitqueue_head(&mousedev->wait); mousedev->minor = minor; @@ -651,8 +719,7 @@ static struct input_handle *mousedev_con mousedev->handle.private = mousedev; sprintf(mousedev->name, "mouse%d", minor); - if (mousedev_mix.open) - input_open_device(&mousedev->handle); + mixdev_add_device(mousedev); mousedev_table[minor] = mousedev; @@ -675,18 +742,18 @@ static void mousedev_disconnect(struct i sysfs_remove_link(&input_class.subsys.kset.kobj, mousedev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + mousedev->minor)); - mousedev->exist = 0; + mixdev_remove_device(mousedev); + + mousedev->exist = 0; if (mousedev->open) { input_close_device(handle); wake_up_interruptible(&mousedev->wait); list_for_each_entry(list, &mousedev->list, node) kill_fasync(&list->fasync, SIGIO, POLL_HUP); - } else { - if (mousedev_mix.open) - input_close_device(handle); - mousedev_free(mousedev); } + + kref_put(&mousedev->kref, mousedev_free); } static const struct input_device_id mousedev_ids[] = { @@ -714,7 +781,7 @@ static const struct input_device_id mous .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) }, }, /* A touchpad */ - { }, /* Terminating entry */ + { }, /* Terminating entry */ }; MODULE_DEVICE_TABLE(input, mousedev_ids); @@ -745,13 +812,14 @@ static int __init mousedev_init(void) if (error) return error; - memset(&mousedev_mix, 0, sizeof(struct mousedev)); + kref_init(&mousedev_mix.kref); INIT_LIST_HEAD(&mousedev_mix.list); init_waitqueue_head(&mousedev_mix.wait); - mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; mousedev_mix.exist = 1; mousedev_mix.minor = MOUSEDEV_MIX; + mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; + cdev = class_device_create(&input_class, NULL, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice"); if (IS_ERR(cdev)) { Index: work/drivers/input/tsdev.c =================================================================== --- work.orig/drivers/input/tsdev.c +++ work/drivers/input/tsdev.c @@ -110,9 +110,11 @@ struct tsdev { int open; int minor; char name[8]; + struct input_handle handle; wait_queue_head_t wait; + struct kref kref; struct list_head list; - struct input_handle handle; + int x, y, pressure; struct ts_calibration cal; }; @@ -150,6 +152,7 @@ static int tsdev_open(struct inode *inod { int i = iminor(inode) - TSDEV_MINOR_BASE; struct tsdev_list *list; + struct tsdev *tsdev; printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled " "for removal.\nSee Documentation/feature-removal-schedule.txt " @@ -158,24 +161,31 @@ static int tsdev_open(struct inode *inod if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK]) return -ENODEV; - if (!(list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL))) + tsdev = tsdev_table[i & TSDEV_MINOR_MASK]; + if (!tsdev || !tsdev->exist) + return -ENODEV; + + list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL); + if (!list) return -ENOMEM; - list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0; + kref_get(&tsdev->kref); - i &= TSDEV_MINOR_MASK; - list->tsdev = tsdev_table[i]; - list_add_tail(&list->node, &tsdev_table[i]->list); + list->tsdev = tsdev; + list->raw = (i >= TSDEV_MINORS / 2); + list_add_tail(&list->node, &tsdev->list); file->private_data = list; - if (!list->tsdev->open++) - if (list->tsdev->exist) - input_open_device(&list->tsdev->handle); + if (!tsdev->open++ && tsdev->exist) + input_open_device(&list->tsdev->handle); + return 0; } -static void tsdev_free(struct tsdev *tsdev) +static void tsdev_free(struct kref *kref) { + struct tsdev *tsdev = container_of(kref, struct tsdev, kref); + tsdev_table[tsdev->minor] = NULL; kfree(tsdev); } @@ -183,17 +193,18 @@ static void tsdev_free(struct tsdev *tsd static int tsdev_release(struct inode *inode, struct file *file) { struct tsdev_list *list = file->private_data; + struct tsdev *tsdev = list->tsdev; tsdev_fasync(-1, file, 0); - list_del(&list->node); - if (!--list->tsdev->open) { - if (list->tsdev->exist) - input_close_device(&list->tsdev->handle); - else - tsdev_free(list->tsdev); - } + list_del(&list->node); kfree(list); + + if (!--tsdev->open && tsdev->exist) + input_close_device(&tsdev->handle); + + kref_put(&tsdev->kref, tsdev_free); + return 0; } @@ -201,22 +212,23 @@ static ssize_t tsdev_read(struct file *f loff_t * ppos) { struct tsdev_list *list = file->private_data; + struct tsdev *tsdev = list->tsdev; int retval = 0; - if (list->head == list->tail && list->tsdev->exist && (file->f_flags & O_NONBLOCK)) + if (list->head == list->tail && tsdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->tsdev->wait, - list->head != list->tail || !list->tsdev->exist); + retval = wait_event_interruptible(tsdev->wait, + list->head != list->tail || !tsdev->exist); if (retval) return retval; - if (!list->tsdev->exist) + if (!tsdev->exist) return -ENODEV; while (list->head != list->tail && - retval + sizeof (struct ts_event) <= count) { + retval + sizeof(struct ts_event) <= count) { if (copy_to_user (buffer + retval, list->event + list->tail, sizeof (struct ts_event))) return -EFAULT; @@ -385,9 +397,11 @@ static struct input_handle *tsdev_connec return NULL; } - if (!(tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL))) + tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL); + if (!tsdev) return NULL; + kref_init(&tsdev->kref); INIT_LIST_HEAD(&tsdev->list); init_waitqueue_head(&tsdev->wait); @@ -441,8 +455,9 @@ static void tsdev_disconnect(struct inpu wake_up_interruptible(&tsdev->wait); list_for_each_entry(list, &tsdev->list, node) kill_fasync(&list->fasync, SIGIO, POLL_HUP); - } else - tsdev_free(tsdev); + } + + kref_put(&tsdev->kref, tsdev_free); } static const struct input_device_id tsdev_ids[] = { - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/