diff options
-rw-r--r-- | drivers/base/core.c | 29 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 20 | ||||
-rw-r--r-- | drivers/s390/cio/ccwgroup.c | 18 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 14 | ||||
-rw-r--r-- | fs/sysfs/file.c | 64 | ||||
-rw-r--r-- | fs/sysfs/inode.c | 10 | ||||
-rw-r--r-- | include/linux/device.h | 2 | ||||
-rw-r--r-- | include/linux/sysfs.h | 9 |
8 files changed, 143 insertions, 23 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index f191afe62b4d..ad0f4a2f25c4 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -407,6 +407,35 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) | |||
407 | } | 407 | } |
408 | EXPORT_SYMBOL_GPL(device_remove_bin_file); | 408 | EXPORT_SYMBOL_GPL(device_remove_bin_file); |
409 | 409 | ||
410 | /** | ||
411 | * device_schedule_callback - helper to schedule a callback for a device | ||
412 | * @dev: device. | ||
413 | * @func: callback function to invoke later. | ||
414 | * | ||
415 | * Attribute methods must not unregister themselves or their parent device | ||
416 | * (which would amount to the same thing). Attempts to do so will deadlock, | ||
417 | * since unregistration is mutually exclusive with driver callbacks. | ||
418 | * | ||
419 | * Instead methods can call this routine, which will attempt to allocate | ||
420 | * and schedule a workqueue request to call back @func with @dev as its | ||
421 | * argument in the workqueue's process context. @dev will be pinned until | ||
422 | * @func returns. | ||
423 | * | ||
424 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | ||
425 | * be allocated. | ||
426 | * | ||
427 | * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an | ||
428 | * underlying sysfs routine (since it is intended for use by attribute | ||
429 | * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. | ||
430 | */ | ||
431 | int device_schedule_callback(struct device *dev, | ||
432 | void (*func)(struct device *)) | ||
433 | { | ||
434 | return sysfs_schedule_callback(&dev->kobj, | ||
435 | (void (*)(void *)) func, dev); | ||
436 | } | ||
437 | EXPORT_SYMBOL_GPL(device_schedule_callback); | ||
438 | |||
410 | static void klist_children_get(struct klist_node *n) | 439 | static void klist_children_get(struct klist_node *n) |
411 | { | 440 | { |
412 | struct device *dev = container_of(n, struct device, knode_parent); | 441 | struct device *dev = container_of(n, struct device, knode_parent); |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 9c8157fb6d75..67f3347afcf3 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <asm/byteorder.h> | 26 | #include <asm/byteorder.h> |
27 | #include <linux/input.h> | 27 | #include <linux/input.h> |
28 | #include <linux/wait.h> | 28 | #include <linux/wait.h> |
29 | #include <linux/vmalloc.h> | ||
29 | 30 | ||
30 | #include <linux/hid.h> | 31 | #include <linux/hid.h> |
31 | #include <linux/hiddev.h> | 32 | #include <linux/hiddev.h> |
@@ -654,12 +655,13 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) | |||
654 | memcpy(device->rdesc, start, size); | 655 | memcpy(device->rdesc, start, size); |
655 | device->rsize = size; | 656 | device->rsize = size; |
656 | 657 | ||
657 | if (!(parser = kzalloc(sizeof(struct hid_parser), GFP_KERNEL))) { | 658 | if (!(parser = vmalloc(sizeof(struct hid_parser)))) { |
658 | kfree(device->rdesc); | 659 | kfree(device->rdesc); |
659 | kfree(device->collection); | 660 | kfree(device->collection); |
660 | kfree(device); | 661 | kfree(device); |
661 | return NULL; | 662 | return NULL; |
662 | } | 663 | } |
664 | memset(parser, 0, sizeof(struct hid_parser)); | ||
663 | parser->device = device; | 665 | parser->device = device; |
664 | 666 | ||
665 | end = start + size; | 667 | end = start + size; |
@@ -668,7 +670,7 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) | |||
668 | if (item.format != HID_ITEM_FORMAT_SHORT) { | 670 | if (item.format != HID_ITEM_FORMAT_SHORT) { |
669 | dbg("unexpected long global item"); | 671 | dbg("unexpected long global item"); |
670 | hid_free_device(device); | 672 | hid_free_device(device); |
671 | kfree(parser); | 673 | vfree(parser); |
672 | return NULL; | 674 | return NULL; |
673 | } | 675 | } |
674 | 676 | ||
@@ -676,7 +678,7 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) | |||
676 | dbg("item %u %u %u %u parsing failed\n", | 678 | dbg("item %u %u %u %u parsing failed\n", |
677 | item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); | 679 | item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); |
678 | hid_free_device(device); | 680 | hid_free_device(device); |
679 | kfree(parser); | 681 | vfree(parser); |
680 | return NULL; | 682 | return NULL; |
681 | } | 683 | } |
682 | 684 | ||
@@ -684,23 +686,23 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) | |||
684 | if (parser->collection_stack_ptr) { | 686 | if (parser->collection_stack_ptr) { |
685 | dbg("unbalanced collection at end of report description"); | 687 | dbg("unbalanced collection at end of report description"); |
686 | hid_free_device(device); | 688 | hid_free_device(device); |
687 | kfree(parser); | 689 | vfree(parser); |
688 | return NULL; | 690 | return NULL; |
689 | } | 691 | } |
690 | if (parser->local.delimiter_depth) { | 692 | if (parser->local.delimiter_depth) { |
691 | dbg("unbalanced delimiter at end of report description"); | 693 | dbg("unbalanced delimiter at end of report description"); |
692 | hid_free_device(device); | 694 | hid_free_device(device); |
693 | kfree(parser); | 695 | vfree(parser); |
694 | return NULL; | 696 | return NULL; |
695 | } | 697 | } |
696 | kfree(parser); | 698 | vfree(parser); |
697 | return device; | 699 | return device; |
698 | } | 700 | } |
699 | } | 701 | } |
700 | 702 | ||
701 | dbg("item fetching failed at offset %d\n", (int)(end - start)); | 703 | dbg("item fetching failed at offset %d\n", (int)(end - start)); |
702 | hid_free_device(device); | 704 | hid_free_device(device); |
703 | kfree(parser); | 705 | vfree(parser); |
704 | return NULL; | 706 | return NULL; |
705 | } | 707 | } |
706 | EXPORT_SYMBOL_GPL(hid_parse_report); | 708 | EXPORT_SYMBOL_GPL(hid_parse_report); |
@@ -872,10 +874,6 @@ static void hid_output_field(struct hid_field *field, __u8 *data) | |||
872 | unsigned size = field->report_size; | 874 | unsigned size = field->report_size; |
873 | unsigned n; | 875 | unsigned n; |
874 | 876 | ||
875 | /* make sure the unused bits in the last byte are zeros */ | ||
876 | if (count > 0 && size > 0) | ||
877 | data[(offset+count*size-1)/8] = 0; | ||
878 | |||
879 | for (n = 0; n < count; n++) { | 877 | for (n = 0; n < count; n++) { |
880 | if (field->logical_minimum < 0) /* signed values */ | 878 | if (field->logical_minimum < 0) /* signed values */ |
881 | implement(data, offset + n * size, size, s32ton(field->value[n], size)); | 879 | implement(data, offset + n * size, size, s32ton(field->value[n], size)); |
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index d48e3ca4752c..5aeb68e732b0 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c | |||
@@ -71,19 +71,31 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) | |||
71 | * Provide an 'ungroup' attribute so the user can remove group devices no | 71 | * Provide an 'ungroup' attribute so the user can remove group devices no |
72 | * longer needed or accidentially created. Saves memory :) | 72 | * longer needed or accidentially created. Saves memory :) |
73 | */ | 73 | */ |
74 | static void ccwgroup_ungroup_callback(struct device *dev) | ||
75 | { | ||
76 | struct ccwgroup_device *gdev = to_ccwgroupdev(dev); | ||
77 | |||
78 | __ccwgroup_remove_symlinks(gdev); | ||
79 | device_unregister(dev); | ||
80 | } | ||
81 | |||
74 | static ssize_t | 82 | static ssize_t |
75 | ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | 83 | ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
76 | { | 84 | { |
77 | struct ccwgroup_device *gdev; | 85 | struct ccwgroup_device *gdev; |
86 | int rc; | ||
78 | 87 | ||
79 | gdev = to_ccwgroupdev(dev); | 88 | gdev = to_ccwgroupdev(dev); |
80 | 89 | ||
81 | if (gdev->state != CCWGROUP_OFFLINE) | 90 | if (gdev->state != CCWGROUP_OFFLINE) |
82 | return -EINVAL; | 91 | return -EINVAL; |
83 | 92 | ||
84 | __ccwgroup_remove_symlinks(gdev); | 93 | /* Note that we cannot unregister the device from one of its |
85 | device_unregister(dev); | 94 | * attribute methods, so we have to use this roundabout approach. |
86 | 95 | */ | |
96 | rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); | ||
97 | if (rc) | ||
98 | count = rc; | ||
87 | return count; | 99 | return count; |
88 | } | 100 | } |
89 | 101 | ||
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index c275dcac3f18..939de0de18bc 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -452,10 +452,22 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, const cha | |||
452 | } | 452 | } |
453 | static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); | 453 | static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); |
454 | 454 | ||
455 | static void sdev_store_delete_callback(struct device *dev) | ||
456 | { | ||
457 | scsi_remove_device(to_scsi_device(dev)); | ||
458 | } | ||
459 | |||
455 | static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, | 460 | static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, |
456 | size_t count) | 461 | size_t count) |
457 | { | 462 | { |
458 | scsi_remove_device(to_scsi_device(dev)); | 463 | int rc; |
464 | |||
465 | /* An attribute cannot be unregistered by one of its own methods, | ||
466 | * so we have to use this roundabout approach. | ||
467 | */ | ||
468 | rc = device_schedule_callback(dev, sdev_store_delete_callback); | ||
469 | if (rc) | ||
470 | count = rc; | ||
459 | return count; | 471 | return count; |
460 | }; | 472 | }; |
461 | static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); | 473 | static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 8d4d839a9d88..fc4633378dc0 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -168,12 +168,12 @@ sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
168 | ssize_t retval = 0; | 168 | ssize_t retval = 0; |
169 | 169 | ||
170 | down(&buffer->sem); | 170 | down(&buffer->sem); |
171 | if (buffer->orphaned) { | ||
172 | retval = -ENODEV; | ||
173 | goto out; | ||
174 | } | ||
175 | if (buffer->needs_read_fill) { | 171 | if (buffer->needs_read_fill) { |
176 | if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) | 172 | if (buffer->orphaned) |
173 | retval = -ENODEV; | ||
174 | else | ||
175 | retval = fill_read_buffer(file->f_path.dentry,buffer); | ||
176 | if (retval) | ||
177 | goto out; | 177 | goto out; |
178 | } | 178 | } |
179 | pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", | 179 | pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", |
@@ -629,6 +629,60 @@ void sysfs_remove_file_from_group(struct kobject *kobj, | |||
629 | } | 629 | } |
630 | EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); | 630 | EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); |
631 | 631 | ||
632 | struct sysfs_schedule_callback_struct { | ||
633 | struct kobject *kobj; | ||
634 | void (*func)(void *); | ||
635 | void *data; | ||
636 | struct work_struct work; | ||
637 | }; | ||
638 | |||
639 | static void sysfs_schedule_callback_work(struct work_struct *work) | ||
640 | { | ||
641 | struct sysfs_schedule_callback_struct *ss = container_of(work, | ||
642 | struct sysfs_schedule_callback_struct, work); | ||
643 | |||
644 | (ss->func)(ss->data); | ||
645 | kobject_put(ss->kobj); | ||
646 | kfree(ss); | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * sysfs_schedule_callback - helper to schedule a callback for a kobject | ||
651 | * @kobj: object we're acting for. | ||
652 | * @func: callback function to invoke later. | ||
653 | * @data: argument to pass to @func. | ||
654 | * | ||
655 | * sysfs attribute methods must not unregister themselves or their parent | ||
656 | * kobject (which would amount to the same thing). Attempts to do so will | ||
657 | * deadlock, since unregistration is mutually exclusive with driver | ||
658 | * callbacks. | ||
659 | * | ||
660 | * Instead methods can call this routine, which will attempt to allocate | ||
661 | * and schedule a workqueue request to call back @func with @data as its | ||
662 | * argument in the workqueue's process context. @kobj will be pinned | ||
663 | * until @func returns. | ||
664 | * | ||
665 | * Returns 0 if the request was submitted, -ENOMEM if storage could not | ||
666 | * be allocated. | ||
667 | */ | ||
668 | int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), | ||
669 | void *data) | ||
670 | { | ||
671 | struct sysfs_schedule_callback_struct *ss; | ||
672 | |||
673 | ss = kmalloc(sizeof(*ss), GFP_KERNEL); | ||
674 | if (!ss) | ||
675 | return -ENOMEM; | ||
676 | kobject_get(kobj); | ||
677 | ss->kobj = kobj; | ||
678 | ss->func = func; | ||
679 | ss->data = data; | ||
680 | INIT_WORK(&ss->work, sysfs_schedule_callback_work); | ||
681 | schedule_work(&ss->work); | ||
682 | return 0; | ||
683 | } | ||
684 | EXPORT_SYMBOL_GPL(sysfs_schedule_callback); | ||
685 | |||
632 | 686 | ||
633 | EXPORT_SYMBOL_GPL(sysfs_create_file); | 687 | EXPORT_SYMBOL_GPL(sysfs_create_file); |
634 | EXPORT_SYMBOL_GPL(sysfs_remove_file); | 688 | EXPORT_SYMBOL_GPL(sysfs_remove_file); |
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index ccb7d722c558..4de5c6b89918 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c | |||
@@ -222,13 +222,17 @@ const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) | |||
222 | 222 | ||
223 | static inline void orphan_all_buffers(struct inode *node) | 223 | static inline void orphan_all_buffers(struct inode *node) |
224 | { | 224 | { |
225 | struct sysfs_buffer_collection *set = node->i_private; | 225 | struct sysfs_buffer_collection *set; |
226 | struct sysfs_buffer *buf; | 226 | struct sysfs_buffer *buf; |
227 | 227 | ||
228 | mutex_lock_nested(&node->i_mutex, I_MUTEX_CHILD); | 228 | mutex_lock_nested(&node->i_mutex, I_MUTEX_CHILD); |
229 | if (node->i_private) { | 229 | set = node->i_private; |
230 | list_for_each_entry(buf, &set->associates, associates) | 230 | if (set) { |
231 | list_for_each_entry(buf, &set->associates, associates) { | ||
232 | down(&buf->sem); | ||
231 | buf->orphaned = 1; | 233 | buf->orphaned = 1; |
234 | up(&buf->sem); | ||
235 | } | ||
232 | } | 236 | } |
233 | mutex_unlock(&node->i_mutex); | 237 | mutex_unlock(&node->i_mutex); |
234 | } | 238 | } |
diff --git a/include/linux/device.h b/include/linux/device.h index 39a3199a826d..caad9bba9652 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
@@ -353,6 +353,8 @@ extern int __must_check device_create_bin_file(struct device *dev, | |||
353 | struct bin_attribute *attr); | 353 | struct bin_attribute *attr); |
354 | extern void device_remove_bin_file(struct device *dev, | 354 | extern void device_remove_bin_file(struct device *dev, |
355 | struct bin_attribute *attr); | 355 | struct bin_attribute *attr); |
356 | extern int device_schedule_callback(struct device *dev, | ||
357 | void (*func)(struct device *)); | ||
356 | 358 | ||
357 | /* device resource management */ | 359 | /* device resource management */ |
358 | typedef void (*dr_release_t)(struct device *dev, void *res); | 360 | typedef void (*dr_release_t)(struct device *dev, void *res); |
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 523405e1e1f6..0544edda7168 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h | |||
@@ -78,6 +78,9 @@ struct sysfs_ops { | |||
78 | 78 | ||
79 | #ifdef CONFIG_SYSFS | 79 | #ifdef CONFIG_SYSFS |
80 | 80 | ||
81 | extern int sysfs_schedule_callback(struct kobject *kobj, | ||
82 | void (*func)(void *), void *data); | ||
83 | |||
81 | extern int __must_check | 84 | extern int __must_check |
82 | sysfs_create_dir(struct kobject *, struct dentry *); | 85 | sysfs_create_dir(struct kobject *, struct dentry *); |
83 | 86 | ||
@@ -132,6 +135,12 @@ extern int __must_check sysfs_init(void); | |||
132 | 135 | ||
133 | #else /* CONFIG_SYSFS */ | 136 | #else /* CONFIG_SYSFS */ |
134 | 137 | ||
138 | static inline int sysfs_schedule_callback(struct kobject *kobj, | ||
139 | void (*func)(void *), void *data) | ||
140 | { | ||
141 | return -ENOSYS; | ||
142 | } | ||
143 | |||
135 | static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow) | 144 | static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow) |
136 | { | 145 | { |
137 | return 0; | 146 | return 0; |