diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-11-19 10:53:40 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-01-24 23:40:36 -0500 |
commit | 09f82ea92822a7bbb7e816508abbda47ed54a77f (patch) | |
tree | 059011e094839a633597c2e88afc40f779894438 /lib | |
parent | 97c146ef075dc40ae34407c03d3860fc3850b8e8 (diff) |
Kobject: drop child->parent ref at unregistration
This patch (as1015) reverts changes that were made to the driver core
about four years ago. The intent back then was to avoid certain kinds
of invalid memory accesses by leaving kernel objects allocated as long
as any of their children were still allocated. The original and
correct approach was to wait only as long as any children were still
_registered_; that's what this patch reinstates.
This fixes a problem in the SCSI core made visible by the class_device
to regular device conversion: A reference loop (scsi_device holds
reference to request_queue, which is the child of a gendisk, which is
the child of the scsi_device) prevents the data structures from being
released, even though they are deregistered okay.
It's possible that this change will cause a few bugs to surface,
things that have been hidden for several years. They can be fixed
easily enough by having the child device take an explicit reference to
the parent whenever needed.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/kobject.c | 7 |
1 files changed, 4 insertions, 3 deletions
diff --git a/lib/kobject.c b/lib/kobject.c index 1015f74212d0..493e991abb1b 100644 --- a/lib/kobject.c +++ b/lib/kobject.c | |||
@@ -149,12 +149,16 @@ void kobject_init(struct kobject * kobj) | |||
149 | 149 | ||
150 | static void unlink(struct kobject * kobj) | 150 | static void unlink(struct kobject * kobj) |
151 | { | 151 | { |
152 | struct kobject *parent = kobj->parent; | ||
153 | |||
152 | if (kobj->kset) { | 154 | if (kobj->kset) { |
153 | spin_lock(&kobj->kset->list_lock); | 155 | spin_lock(&kobj->kset->list_lock); |
154 | list_del_init(&kobj->entry); | 156 | list_del_init(&kobj->entry); |
155 | spin_unlock(&kobj->kset->list_lock); | 157 | spin_unlock(&kobj->kset->list_lock); |
156 | } | 158 | } |
159 | kobj->parent = NULL; | ||
157 | kobject_put(kobj); | 160 | kobject_put(kobj); |
161 | kobject_put(parent); | ||
158 | } | 162 | } |
159 | 163 | ||
160 | /** | 164 | /** |
@@ -208,7 +212,6 @@ int kobject_add(struct kobject * kobj) | |||
208 | if (error) { | 212 | if (error) { |
209 | /* unlink does the kobject_put() for us */ | 213 | /* unlink does the kobject_put() for us */ |
210 | unlink(kobj); | 214 | unlink(kobj); |
211 | kobject_put(parent); | ||
212 | 215 | ||
213 | /* be noisy on error issues */ | 216 | /* be noisy on error issues */ |
214 | if (error == -EEXIST) | 217 | if (error == -EEXIST) |
@@ -590,7 +593,6 @@ static void kobject_cleanup(struct kobject *kobj) | |||
590 | { | 593 | { |
591 | struct kobj_type * t = get_ktype(kobj); | 594 | struct kobj_type * t = get_ktype(kobj); |
592 | struct kset * s = kobj->kset; | 595 | struct kset * s = kobj->kset; |
593 | struct kobject * parent = kobj->parent; | ||
594 | const char *name = kobj->k_name; | 596 | const char *name = kobj->k_name; |
595 | 597 | ||
596 | pr_debug("kobject: '%s' (%p): %s\n", | 598 | pr_debug("kobject: '%s' (%p): %s\n", |
@@ -604,7 +606,6 @@ static void kobject_cleanup(struct kobject *kobj) | |||
604 | } | 606 | } |
605 | if (s) | 607 | if (s) |
606 | kset_put(s); | 608 | kset_put(s); |
607 | kobject_put(parent); | ||
608 | } | 609 | } |
609 | 610 | ||
610 | static void kobject_release(struct kref *kref) | 611 | static void kobject_release(struct kref *kref) |