aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/core.c29
-rw-r--r--drivers/hid/hid-core.c20
-rw-r--r--drivers/s390/cio/ccwgroup.c18
-rw-r--r--drivers/scsi/scsi_sysfs.c14
-rw-r--r--fs/sysfs/file.c64
-rw-r--r--fs/sysfs/inode.c10
-rw-r--r--include/linux/device.h2
-rw-r--r--include/linux/sysfs.h9
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}
408EXPORT_SYMBOL_GPL(device_remove_bin_file); 408EXPORT_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 */
431int 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}
437EXPORT_SYMBOL_GPL(device_schedule_callback);
438
410static void klist_children_get(struct klist_node *n) 439static 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}
706EXPORT_SYMBOL_GPL(hid_parse_report); 708EXPORT_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 */
74static 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
74static ssize_t 82static ssize_t
75ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 83ccwgroup_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}
453static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); 453static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
454 454
455static void sdev_store_delete_callback(struct device *dev)
456{
457 scsi_remove_device(to_scsi_device(dev));
458}
459
455static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, 460static 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};
461static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); 473static 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}
630EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); 630EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
631 631
632struct sysfs_schedule_callback_struct {
633 struct kobject *kobj;
634 void (*func)(void *);
635 void *data;
636 struct work_struct work;
637};
638
639static 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 */
668int 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}
684EXPORT_SYMBOL_GPL(sysfs_schedule_callback);
685
632 686
633EXPORT_SYMBOL_GPL(sysfs_create_file); 687EXPORT_SYMBOL_GPL(sysfs_create_file);
634EXPORT_SYMBOL_GPL(sysfs_remove_file); 688EXPORT_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
223static inline void orphan_all_buffers(struct inode *node) 223static 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);
354extern void device_remove_bin_file(struct device *dev, 354extern void device_remove_bin_file(struct device *dev,
355 struct bin_attribute *attr); 355 struct bin_attribute *attr);
356extern int device_schedule_callback(struct device *dev,
357 void (*func)(struct device *));
356 358
357/* device resource management */ 359/* device resource management */
358typedef void (*dr_release_t)(struct device *dev, void *res); 360typedef 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
81extern int sysfs_schedule_callback(struct kobject *kobj,
82 void (*func)(void *), void *data);
83
81extern int __must_check 84extern int __must_check
82sysfs_create_dir(struct kobject *, struct dentry *); 85sysfs_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
138static inline int sysfs_schedule_callback(struct kobject *kobj,
139 void (*func)(void *), void *data)
140{
141 return -ENOSYS;
142}
143
135static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow) 144static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow)
136{ 145{
137 return 0; 146 return 0;