On Thu, 2007-03-29 at 22:18 -0700, Greg KH wrote:
> On Wed, Mar 28, 2007 at 02:49:12PM +0800, Shaohua Li wrote:
> > On Tue, 2007-03-27 at 22:58 -0700, Greg KH wrote:
> > > On Wed, Mar 28, 2007 at 01:39:26PM +0800, Shaohua Li wrote:
> > > > On Tue, 2007-03-27 at 22:27 -0700, Greg KH wrote:
> > > > >
> > > > > Putting more than one kobject in the same structure is a broken
> > > > > design.
> > > > > How can you control the lifetime rules properly if there are two
> > > > > reference counts for the same structure? It doesn't work.
> > > > >
> > > > > If you really need something like this, then just use a pointer to a
> > > > > kobject for one of them instead of embedding it. Why do you need two
> > > > > different kobjects here?
> > > > Our data structure is something like below:
> > > >
> > > > struct foo {
> > > > kobject kobja;
> > > > }
> > > >
> > > > struct bar {
> > > > struct foo foo[];
> > >
> > > Ick, don't do that...
> > why?
> > > > kobject kobjb
>
> Because you have multiple kobjects in the same object.
>
> It's just that simple, the lifetime rules for such a thing is almost
> impossible to track properly. Don't do it!
>
> > > > }
> > > >
> > > > kobjb's .release will free struct bar. kobjb is the parent of kobja. if
> > > > you have a reference on kobja, then kobjb can't be released too, right?
> > > > So we only kobjb provide a .release to free the memory, kobja's .release
> > > > isn't required.
> > >
> > > Why not just use the "normal" parent/child relationship with the
> > > kobjects like the rest of the kernel does?
> > I still didn't get the reason why we couldn't do this in the way of my
> > patch. As I said, there isn't risk to use 'freed memory'. I can make the
> > 'struct foo' a pointer, but this will mess the cpuidle driver.
>
> Again, the main point is you can not have more than one reference count
> for the same structure. It just does not work at all.
>
> So please, fix the code, it is broken.
>
> And yes, I know of other places in the kernel (scsi stack...) that
> violate this, but that only means that they are wrong, not that it is an
> excuse for you to do it also.
We don't use the kobject to track the reference count. But anyway, below
patch should make you happy.
Signed-off-by: Shaohua Li <[EMAIL PROTECTED]>
Index: rc4-mm1/drivers/acpi/processor_idle.c
===================================================================
--- rc4-mm1.orig/drivers/acpi/processor_idle.c 2007-03-23 08:58:51.000000000
+0800
+++ rc4-mm1/drivers/acpi/processor_idle.c 2007-03-28 09:23:24.000000000
+0800
@@ -623,7 +623,7 @@ int acpi_processor_cst_has_changed(struc
return -ENODEV;
acpi_processor_get_power_info(pr);
- return cpuidle_force_redetect(&per_cpu(cpuidle_devices, pr->id));
+ return cpuidle_force_redetect(per_cpu(cpuidle_devices, pr->id));
}
/* proc interface */
Index: rc4-mm1/drivers/cpuidle/cpuidle.c
===================================================================
--- rc4-mm1.orig/drivers/cpuidle/cpuidle.c 2007-03-23 09:52:48.000000000
+0800
+++ rc4-mm1/drivers/cpuidle/cpuidle.c 2007-03-28 09:22:41.000000000 +0800
@@ -18,7 +18,7 @@
#include "cpuidle.h"
-DEFINE_PER_CPU(struct cpuidle_device, cpuidle_devices);
+DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
EXPORT_PER_CPU_SYMBOL_GPL(cpuidle_devices);
DEFINE_MUTEX(cpuidle_lock);
@@ -34,7 +34,7 @@ static void (*pm_idle_old)(void);
*/
static void cpuidle_idle_call(void)
{
- struct cpuidle_device *dev = &__get_cpu_var(cpuidle_devices);
+ struct cpuidle_device *dev = __get_cpu_var(cpuidle_devices);
struct cpuidle_state *target_state;
int next_state;
@@ -117,7 +117,7 @@ static int cpuidle_add_device(struct sys
int cpu = sys_dev->id;
struct cpuidle_device *dev;
- dev = &per_cpu(cpuidle_devices, cpu);
+ dev = per_cpu(cpuidle_devices, cpu);
mutex_lock(&cpuidle_lock);
if (cpu_is_offline(cpu)) {
@@ -125,6 +125,16 @@ static int cpuidle_add_device(struct sys
return 0;
}
+ if (!dev) {
+ dev = kzalloc(sizeof(struct cpuidle_device), GFP_KERNEL);
+ if (!dev) {
+ mutex_unlock(&cpuidle_lock);
+ return -ENOMEM;
+ }
+ init_completion(&dev->kobj_unregister);
+ per_cpu(cpuidle_devices, cpu) = dev;
+ }
+
if (dev->status & CPUIDLE_STATUS_DETECTED) {
mutex_unlock(&cpuidle_lock);
return 0;
@@ -153,7 +163,7 @@ static int __cpuidle_remove_device(struc
{
struct cpuidle_device *dev;
- dev = &per_cpu(cpuidle_devices, sys_dev->id);
+ dev = per_cpu(cpuidle_devices, sys_dev->id);
if (!(dev->status & CPUIDLE_STATUS_DETECTED)) {
return 0;
@@ -166,6 +176,9 @@ static int __cpuidle_remove_device(struc
cpuidle_detach_driver(dev);
cpuidle_remove_sysfs(sys_dev);
list_del(&dev->device_list);
+ wait_for_completion(&dev->kobj_unregister);
+ per_cpu(cpuidle_devices, sys_dev->id) = NULL;
+ kfree(dev);
return 0;
}
Index: rc4-mm1/drivers/cpuidle/sysfs.c
===================================================================
--- rc4-mm1.orig/drivers/cpuidle/sysfs.c 2007-03-23 09:34:01.000000000
+0800
+++ rc4-mm1/drivers/cpuidle/sysfs.c 2007-03-30 14:22:50.000000000 +0800
@@ -210,8 +210,16 @@ static struct sysfs_ops cpuidle_sysfs_op
.store = cpuidle_store,
};
+static void cpuidle_sysfs_release(struct kobject *kobj)
+{
+ struct cpuidle_device *dev = kobj_to_cpuidledev(kobj);
+
+ complete(&dev->kobj_unregister);
+}
+
static struct kobj_type ktype_cpuidle = {
.sysfs_ops = &cpuidle_sysfs_ops,
+ .release = cpuidle_sysfs_release,
};
struct cpuidle_state_attr {
@@ -246,7 +254,8 @@ static struct attribute *cpuidle_state_d
NULL
};
-#define kobj_to_state(k) container_of(k, struct cpuidle_state, kobj)
+#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
+#define kobj_to_state(k) (kobj_to_state_obj(k)->state)
#define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr)
static ssize_t cpuidle_state_show(struct kobject * kobj,
struct attribute * attr ,char * buf)
@@ -265,11 +274,27 @@ static struct sysfs_ops cpuidle_state_sy
.show = cpuidle_state_show,
};
+static void cpuidle_state_sysfs_release(struct kobject *kobj)
+{
+ struct cpuidle_state_kobj *state_obj = kobj_to_state_obj(kobj);
+
+ complete(&state_obj->kobj_unregister);
+}
+
static struct kobj_type ktype_state_cpuidle = {
.sysfs_ops = &cpuidle_state_sysfs_ops,
.default_attrs = cpuidle_state_default_attrs,
+ .release = cpuidle_state_sysfs_release,
};
+static void inline cpuidle_free_state_kobj(struct cpuidle_device *device, int
i)
+{
+ kobject_unregister(&device->kobjs[i]->kobj);
+ wait_for_completion(&device->kobjs[i]->kobj_unregister);
+ kfree(device->kobjs[i]);
+ device->kobjs[i] = NULL;
+}
+
/**
* cpuidle_add_driver_sysfs - adds driver-specific sysfs attributes
* @device: the target device
@@ -277,24 +302,32 @@ static struct kobj_type ktype_state_cpui
int cpuidle_add_driver_sysfs(struct cpuidle_device *device)
{
int i, ret;
- struct cpuidle_state *state;
+ struct cpuidle_state_kobj *kobj;
/* state statistics */
for (i = 0; i < device->state_count; i++) {
- state = &device->states[i];
- state->kobj.parent = &device->kobj;
- state->kobj.ktype = &ktype_state_cpuidle;
- kobject_set_name(&state->kobj, "state%d", i);
- ret = kobject_register(&state->kobj);
- if (ret)
+ kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL);
+ if (!kobj)
+ goto error_state;
+ kobj->state = &device->states[i];
+ init_completion(&kobj->kobj_unregister);
+
+ kobj->kobj.parent = &device->kobj;
+ kobj->kobj.ktype = &ktype_state_cpuidle;
+ kobject_set_name(&kobj->kobj, "state%d", i);
+ ret = kobject_register(&kobj->kobj);
+ if (ret) {
+ kfree(kobj);
goto error_state;
+ }
+ device->kobjs[i] = kobj;
}
return 0;
error_state:
for (i = i - 1; i >= 0; i--)
- kobject_unregister(&device->states[i].kobj);
+ cpuidle_free_state_kobj(device, i);
return ret;
}
@@ -307,7 +340,7 @@ void cpuidle_remove_driver_sysfs(struct
int i;
for (i = 0; i < device->state_count; i++)
- kobject_unregister(&device->states[i].kobj);
+ cpuidle_free_state_kobj(device, i);
}
/**
@@ -319,7 +352,7 @@ int cpuidle_add_sysfs(struct sys_device
int cpu = sysdev->id;
struct cpuidle_device *dev;
- dev = &per_cpu(cpuidle_devices, cpu);
+ dev = per_cpu(cpuidle_devices, cpu);
dev->kobj.parent = &sysdev->kobj;
dev->kobj.ktype = &ktype_cpuidle;
kobject_set_name(&dev->kobj, "%s", "cpuidle");
@@ -335,6 +368,6 @@ void cpuidle_remove_sysfs(struct sys_dev
int cpu = sysdev->id;
struct cpuidle_device *dev;
- dev = &per_cpu(cpuidle_devices, cpu);
+ dev = per_cpu(cpuidle_devices, cpu);
kobject_unregister(&dev->kobj);
}
Index: rc4-mm1/include/linux/cpuidle.h
===================================================================
--- rc4-mm1.orig/include/linux/cpuidle.h 2007-03-23 09:55:13.000000000
+0800
+++ rc4-mm1/include/linux/cpuidle.h 2007-03-30 14:05:28.000000000 +0800
@@ -41,8 +41,6 @@ struct cpuidle_state {
int (*enter) (struct cpuidle_device *dev,
struct cpuidle_state *state);
-
- struct kobject kobj;
};
/* Idle State Flags */
@@ -74,6 +72,12 @@ cpuidle_set_statedata(struct cpuidle_sta
state->driver_data = data;
}
+struct cpuidle_state_kobj {
+ struct cpuidle_state *state;
+ struct completion kobj_unregister;
+ struct kobject kobj;
+};
+
struct cpuidle_device {
unsigned int status;
int cpu;
@@ -81,6 +85,7 @@ struct cpuidle_device {
int last_residency;
int state_count;
struct cpuidle_state states[CPUIDLE_STATE_MAX];
+ struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
struct cpuidle_state *last_state;
struct list_head device_list;
@@ -89,9 +94,7 @@ struct cpuidle_device {
void *governor_data;
};
-#define to_cpuidle_device(n) container_of(n, struct cpuidle_device, kobj);
-
-DECLARE_PER_CPU(struct cpuidle_device, cpuidle_devices);
+DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
/* Device Status Flags */
#define CPUIDLE_STATUS_DETECTED (0x1)
-
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html