aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org.(none)>2005-04-19 16:14:28 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org.(none)>2005-04-19 16:14:28 -0400
commit9f6c6fc505560465be0964eb4da1b6ca97bd3951 (patch)
tree9660991b5e417ad7bb74e105c037ff358f60ba27
parentc3c661932cd53582c5b03692b99649300977248a (diff)
parentf0e035f4b6940aae6836500b642029c289ed4535 (diff)
Merge with kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6.git/
for 13 driver core, sysfs, and debugfs fixes.
-rw-r--r--Documentation/kref.txt216
-rw-r--r--drivers/base/class.c2
-rw-r--r--drivers/base/core.c3
-rw-r--r--drivers/base/firmware_class.c3
-rw-r--r--drivers/base/platform.c1
-rw-r--r--drivers/usb/host/hc_crisv10.c1
-rw-r--r--fs/partitions/check.c2
-rw-r--r--fs/sysfs/file.c35
-rw-r--r--include/linux/debugfs.h13
-rw-r--r--include/linux/sysfs.h7
-rw-r--r--lib/kobject.c7
-rw-r--r--net/bridge/br_sysfs_if.c2
-rwxr-xr-xscripts/ver_linux2
13 files changed, 286 insertions, 8 deletions
diff --git a/Documentation/kref.txt b/Documentation/kref.txt
new file mode 100644
index 000000000000..42fe28445916
--- /dev/null
+++ b/Documentation/kref.txt
@@ -0,0 +1,216 @@
1
2krefs allow you to add reference counters to your objects. If you
3have objects that are used in multiple places and passed around, and
4you don't have refcounts, your code is almost certainly broken. If
5you want refcounts, krefs are the way to go.
6
7To use a kref, add one to your data structures like:
8
9struct my_data
10{
11 .
12 .
13 struct kref refcount;
14 .
15 .
16};
17
18The kref can occur anywhere within the data structure.
19
20You must initialize the kref after you allocate it. To do this, call
21kref_init as so:
22
23 struct my_data *data;
24
25 data = kmalloc(sizeof(*data), GFP_KERNEL);
26 if (!data)
27 return -ENOMEM;
28 kref_init(&data->refcount);
29
30This sets the refcount in the kref to 1.
31
32Once you have an initialized kref, you must follow the following
33rules:
34
351) If you make a non-temporary copy of a pointer, especially if
36 it can be passed to another thread of execution, you must
37 increment the refcount with kref_get() before passing it off:
38 kref_get(&data->refcount);
39 If you already have a valid pointer to a kref-ed structure (the
40 refcount cannot go to zero) you may do this without a lock.
41
422) When you are done with a pointer, you must call kref_put():
43 kref_put(&data->refcount, data_release);
44 If this is the last reference to the pointer, the release
45 routine will be called. If the code never tries to get
46 a valid pointer to a kref-ed structure without already
47 holding a valid pointer, it is safe to do this without
48 a lock.
49
503) If the code attempts to gain a reference to a kref-ed structure
51 without already holding a valid pointer, it must serialize access
52 where a kref_put() cannot occur during the kref_get(), and the
53 structure must remain valid during the kref_get().
54
55For example, if you allocate some data and then pass it to another
56thread to process:
57
58void data_release(struct kref *ref)
59{
60 struct my_data *data = container_of(ref, struct my_data, refcount);
61 kfree(data);
62}
63
64void more_data_handling(void *cb_data)
65{
66 struct my_data *data = cb_data;
67 .
68 . do stuff with data here
69 .
70 kref_put(data, data_release);
71}
72
73int my_data_handler(void)
74{
75 int rv = 0;
76 struct my_data *data;
77 struct task_struct *task;
78 data = kmalloc(sizeof(*data), GFP_KERNEL);
79 if (!data)
80 return -ENOMEM;
81 kref_init(&data->refcount);
82
83 kref_get(&data->refcount);
84 task = kthread_run(more_data_handling, data, "more_data_handling");
85 if (task == ERR_PTR(-ENOMEM)) {
86 rv = -ENOMEM;
87 kref_put(&data->refcount, data_release);
88 goto out;
89 }
90
91 .
92 . do stuff with data here
93 .
94 out:
95 kref_put(&data->refcount, data_release);
96 return rv;
97}
98
99This way, it doesn't matter what order the two threads handle the
100data, the kref_put() handles knowing when the data is not referenced
101any more and releasing it. The kref_get() does not require a lock,
102since we already have a valid pointer that we own a refcount for. The
103put needs no lock because nothing tries to get the data without
104already holding a pointer.
105
106Note that the "before" in rule 1 is very important. You should never
107do something like:
108
109 task = kthread_run(more_data_handling, data, "more_data_handling");
110 if (task == ERR_PTR(-ENOMEM)) {
111 rv = -ENOMEM;
112 goto out;
113 } else
114 /* BAD BAD BAD - get is after the handoff */
115 kref_get(&data->refcount);
116
117Don't assume you know what you are doing and use the above construct.
118First of all, you may not know what you are doing. Second, you may
119know what you are doing (there are some situations where locking is
120involved where the above may be legal) but someone else who doesn't
121know what they are doing may change the code or copy the code. It's
122bad style. Don't do it.
123
124There are some situations where you can optimize the gets and puts.
125For instance, if you are done with an object and enqueuing it for
126something else or passing it off to something else, there is no reason
127to do a get then a put:
128
129 /* Silly extra get and put */
130 kref_get(&obj->ref);
131 enqueue(obj);
132 kref_put(&obj->ref, obj_cleanup);
133
134Just do the enqueue. A comment about this is always welcome:
135
136 enqueue(obj);
137 /* We are done with obj, so we pass our refcount off
138 to the queue. DON'T TOUCH obj AFTER HERE! */
139
140The last rule (rule 3) is the nastiest one to handle. Say, for
141instance, you have a list of items that are each kref-ed, and you wish
142to get the first one. You can't just pull the first item off the list
143and kref_get() it. That violates rule 3 because you are not already
144holding a valid pointer. You must add locks or semaphores. For
145instance:
146
147static DECLARE_MUTEX(sem);
148static LIST_HEAD(q);
149struct my_data
150{
151 struct kref refcount;
152 struct list_head link;
153};
154
155static struct my_data *get_entry()
156{
157 struct my_data *entry = NULL;
158 down(&sem);
159 if (!list_empty(&q)) {
160 entry = container_of(q.next, struct my_q_entry, link);
161 kref_get(&entry->refcount);
162 }
163 up(&sem);
164 return entry;
165}
166
167static void release_entry(struct kref *ref)
168{
169 struct my_data *entry = container_of(ref, struct my_data, refcount);
170
171 list_del(&entry->link);
172 kfree(entry);
173}
174
175static void put_entry(struct my_data *entry)
176{
177 down(&sem);
178 kref_put(&entry->refcount, release_entry);
179 up(&sem);
180}
181
182The kref_put() return value is useful if you do not want to hold the
183lock during the whole release operation. Say you didn't want to call
184kfree() with the lock held in the example above (since it is kind of
185pointless to do so). You could use kref_put() as follows:
186
187static void release_entry(struct kref *ref)
188{
189 /* All work is done after the return from kref_put(). */
190}
191
192static void put_entry(struct my_data *entry)
193{
194 down(&sem);
195 if (kref_put(&entry->refcount, release_entry)) {
196 list_del(&entry->link);
197 up(&sem);
198 kfree(entry);
199 } else
200 up(&sem);
201}
202
203This is really more useful if you have to call other routines as part
204of the free operations that could take a long time or might claim the
205same lock. Note that doing everything in the release routine is still
206preferred as it is a little neater.
207
208
209Corey Minyard <minyard@acm.org>
210
211A lot of this was lifted from Greg Kroah-Hartman's 2004 OLS paper and
212presentation on krefs, which can be found at:
213 http://www.kroah.com/linux/talks/ols_2004_kref_paper/Reprint-Kroah-Hartman-OLS2004.pdf
214and:
215 http://www.kroah.com/linux/talks/ols_2004_kref_talk/
216
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 6bf650fce78c..d2a2f8f2b4ed 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -430,6 +430,7 @@ int class_device_add(struct class_device *class_dev)
430 sysfs_create_link(&class_dev->kobj, 430 sysfs_create_link(&class_dev->kobj,
431 &class_dev->dev->kobj, "device"); 431 &class_dev->dev->kobj, "device");
432 432
433 kobject_hotplug(&class_dev->kobj, KOBJ_ADD);
433 register_done: 434 register_done:
434 if (error && parent) 435 if (error && parent)
435 class_put(parent); 436 class_put(parent);
@@ -461,6 +462,7 @@ void class_device_del(struct class_device *class_dev)
461 sysfs_remove_link(&class_dev->kobj, "device"); 462 sysfs_remove_link(&class_dev->kobj, "device");
462 class_device_remove_attrs(class_dev); 463 class_device_remove_attrs(class_dev);
463 464
465 kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE);
464 kobject_del(&class_dev->kobj); 466 kobject_del(&class_dev->kobj);
465 467
466 if (parent) 468 if (parent)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 4e6cce8e6d35..a7cedd8cefe5 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -260,6 +260,8 @@ int device_add(struct device *dev)
260 /* notify platform of device entry */ 260 /* notify platform of device entry */
261 if (platform_notify) 261 if (platform_notify)
262 platform_notify(dev); 262 platform_notify(dev);
263
264 kobject_hotplug(&dev->kobj, KOBJ_ADD);
263 Done: 265 Done:
264 put_device(dev); 266 put_device(dev);
265 return error; 267 return error;
@@ -349,6 +351,7 @@ void device_del(struct device * dev)
349 platform_notify_remove(dev); 351 platform_notify_remove(dev);
350 bus_remove_device(dev); 352 bus_remove_device(dev);
351 device_pm_remove(dev); 353 device_pm_remove(dev);
354 kobject_hotplug(&dev->kobj, KOBJ_REMOVE);
352 kobject_del(&dev->kobj); 355 kobject_del(&dev->kobj);
353 if (parent) 356 if (parent)
354 put_device(parent); 357 put_device(parent);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 26c9464af80a..97fe13f7f07c 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -102,6 +102,9 @@ firmware_class_hotplug(struct class_device *class_dev, char **envp,
102 if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &len, 102 if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &len,
103 "FIRMWARE=%s", fw_priv->fw_id)) 103 "FIRMWARE=%s", fw_priv->fw_id))
104 return -ENOMEM; 104 return -ENOMEM;
105 if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &len,
106 "TIMEOUT=%i", loading_timeout))
107 return -ENOMEM;
105 108
106 envp[i] = NULL; 109 envp[i] = NULL;
107 110
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 996cbb4d5087..cd6453905a9b 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -341,6 +341,7 @@ EXPORT_SYMBOL_GPL(dma_get_required_mask);
341 341
342EXPORT_SYMBOL_GPL(platform_bus); 342EXPORT_SYMBOL_GPL(platform_bus);
343EXPORT_SYMBOL_GPL(platform_bus_type); 343EXPORT_SYMBOL_GPL(platform_bus_type);
344EXPORT_SYMBOL_GPL(platform_add_devices);
344EXPORT_SYMBOL_GPL(platform_device_register); 345EXPORT_SYMBOL_GPL(platform_device_register);
345EXPORT_SYMBOL_GPL(platform_device_register_simple); 346EXPORT_SYMBOL_GPL(platform_device_register_simple);
346EXPORT_SYMBOL_GPL(platform_device_unregister); 347EXPORT_SYMBOL_GPL(platform_device_unregister);
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
index 4b12be822bd4..376f8a034f65 100644
--- a/drivers/usb/host/hc_crisv10.c
+++ b/drivers/usb/host/hc_crisv10.c
@@ -4396,6 +4396,7 @@ static int __init etrax_usb_hc_init(void)
4396 device_initialize(&fake_device); 4396 device_initialize(&fake_device);
4397 kobject_set_name(&fake_device.kobj, "etrax_usb"); 4397 kobject_set_name(&fake_device.kobj, "etrax_usb");
4398 kobject_add(&fake_device.kobj); 4398 kobject_add(&fake_device.kobj);
4399 kobject_hotplug(&fake_device.kobj, KOBJ_ADD);
4399 hc->bus->controller = &fake_device; 4400 hc->bus->controller = &fake_device;
4400 usb_register_bus(hc->bus); 4401 usb_register_bus(hc->bus);
4401 4402
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 31cff785b3bd..2cab98a9a621 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -337,6 +337,7 @@ void register_disk(struct gendisk *disk)
337 if ((err = kobject_add(&disk->kobj))) 337 if ((err = kobject_add(&disk->kobj)))
338 return; 338 return;
339 disk_sysfs_symlinks(disk); 339 disk_sysfs_symlinks(disk);
340 kobject_hotplug(&disk->kobj, KOBJ_ADD);
340 341
341 /* No minors to use for partitions */ 342 /* No minors to use for partitions */
342 if (disk->minors == 1) { 343 if (disk->minors == 1) {
@@ -441,5 +442,6 @@ void del_gendisk(struct gendisk *disk)
441 sysfs_remove_link(&disk->driverfs_dev->kobj, "block"); 442 sysfs_remove_link(&disk->driverfs_dev->kobj, "block");
442 put_device(disk->driverfs_dev); 443 put_device(disk->driverfs_dev);
443 } 444 }
445 kobject_hotplug(&disk->kobj, KOBJ_REMOVE);
444 kobject_del(&disk->kobj); 446 kobject_del(&disk->kobj);
445} 447}
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 352f966a1174..da25aeb0e062 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -428,6 +428,41 @@ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr)
428 428
429 429
430/** 430/**
431 * sysfs_chmod_file - update the modified mode value on an object attribute.
432 * @kobj: object we're acting for.
433 * @attr: attribute descriptor.
434 * @mode: file permissions.
435 *
436 */
437int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode)
438{
439 struct dentry *dir = kobj->dentry;
440 struct dentry *victim;
441 struct sysfs_dirent *sd;
442 umode_t umode = (mode & S_IALLUGO) | S_IFREG;
443 int res = -ENOENT;
444
445 down(&dir->d_inode->i_sem);
446 victim = sysfs_get_dentry(dir, attr->name);
447 if (!IS_ERR(victim)) {
448 if (victim->d_inode &&
449 (victim->d_parent->d_inode == dir->d_inode)) {
450 sd = victim->d_fsdata;
451 attr->mode = mode;
452 sd->s_mode = umode;
453 victim->d_inode->i_mode = umode;
454 dput(victim);
455 res = 0;
456 }
457 }
458 up(&dir->d_inode->i_sem);
459
460 return res;
461}
462EXPORT_SYMBOL_GPL(sysfs_chmod_file);
463
464
465/**
431 * sysfs_remove_file - remove an object attribute. 466 * sysfs_remove_file - remove an object attribute.
432 * @kobj: object we're acting for. 467 * @kobj: object we're acting for.
433 * @attr: attribute descriptor. 468 * @attr: attribute descriptor.
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index f7a7b86f6eef..a5fa6a6eede8 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -17,6 +17,10 @@
17 17
18#include <linux/fs.h> 18#include <linux/fs.h>
19 19
20#include <linux/types.h>
21
22struct file_operations;
23
20#if defined(CONFIG_DEBUG_FS) 24#if defined(CONFIG_DEBUG_FS)
21struct dentry *debugfs_create_file(const char *name, mode_t mode, 25struct dentry *debugfs_create_file(const char *name, mode_t mode,
22 struct dentry *parent, void *data, 26 struct dentry *parent, void *data,
@@ -36,6 +40,9 @@ struct dentry *debugfs_create_bool(const char *name, mode_t mode,
36 struct dentry *parent, u32 *value); 40 struct dentry *parent, u32 *value);
37 41
38#else 42#else
43
44#include <linux/err.h>
45
39/* 46/*
40 * We do not return NULL from these functions if CONFIG_DEBUG_FS is not enabled 47 * We do not return NULL from these functions if CONFIG_DEBUG_FS is not enabled
41 * so users have a chance to detect if there was a real error or not. We don't 48 * so users have a chance to detect if there was a real error or not. We don't
@@ -68,21 +75,21 @@ static inline struct dentry *debugfs_create_u8(const char *name, mode_t mode,
68 75
69static inline struct dentry *debugfs_create_u16(const char *name, mode_t mode, 76static inline struct dentry *debugfs_create_u16(const char *name, mode_t mode,
70 struct dentry *parent, 77 struct dentry *parent,
71 u8 *value) 78 u16 *value)
72{ 79{
73 return ERR_PTR(-ENODEV); 80 return ERR_PTR(-ENODEV);
74} 81}
75 82
76static inline struct dentry *debugfs_create_u32(const char *name, mode_t mode, 83static inline struct dentry *debugfs_create_u32(const char *name, mode_t mode,
77 struct dentry *parent, 84 struct dentry *parent,
78 u8 *value) 85 u32 *value)
79{ 86{
80 return ERR_PTR(-ENODEV); 87 return ERR_PTR(-ENODEV);
81} 88}
82 89
83static inline struct dentry *debugfs_create_bool(const char *name, mode_t mode, 90static inline struct dentry *debugfs_create_bool(const char *name, mode_t mode,
84 struct dentry *parent, 91 struct dentry *parent,
85 u8 *value) 92 u32 *value)
86{ 93{
87 return ERR_PTR(-ENODEV); 94 return ERR_PTR(-ENODEV);
88} 95}
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 6f502ff7902a..38b58b30814a 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -99,6 +99,9 @@ sysfs_create_file(struct kobject *, const struct attribute *);
99extern int 99extern int
100sysfs_update_file(struct kobject *, const struct attribute *); 100sysfs_update_file(struct kobject *, const struct attribute *);
101 101
102extern int
103sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode);
104
102extern void 105extern void
103sysfs_remove_file(struct kobject *, const struct attribute *); 106sysfs_remove_file(struct kobject *, const struct attribute *);
104 107
@@ -140,6 +143,10 @@ static inline int sysfs_update_file(struct kobject * k, const struct attribute *
140{ 143{
141 return 0; 144 return 0;
142} 145}
146static inline int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode)
147{
148 return 0;
149}
143 150
144static inline void sysfs_remove_file(struct kobject * k, const struct attribute * a) 151static inline void sysfs_remove_file(struct kobject * k, const struct attribute * a)
145{ 152{
diff --git a/lib/kobject.c b/lib/kobject.c
index ff9491986b38..5df8441c44e7 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -184,8 +184,6 @@ int kobject_add(struct kobject * kobj)
184 unlink(kobj); 184 unlink(kobj);
185 if (parent) 185 if (parent)
186 kobject_put(parent); 186 kobject_put(parent);
187 } else {
188 kobject_hotplug(kobj, KOBJ_ADD);
189 } 187 }
190 188
191 return error; 189 return error;
@@ -207,7 +205,8 @@ int kobject_register(struct kobject * kobj)
207 printk("kobject_register failed for %s (%d)\n", 205 printk("kobject_register failed for %s (%d)\n",
208 kobject_name(kobj),error); 206 kobject_name(kobj),error);
209 dump_stack(); 207 dump_stack();
210 } 208 } else
209 kobject_hotplug(kobj, KOBJ_ADD);
211 } else 210 } else
212 error = -EINVAL; 211 error = -EINVAL;
213 return error; 212 return error;
@@ -301,7 +300,6 @@ int kobject_rename(struct kobject * kobj, char *new_name)
301 300
302void kobject_del(struct kobject * kobj) 301void kobject_del(struct kobject * kobj)
303{ 302{
304 kobject_hotplug(kobj, KOBJ_REMOVE);
305 sysfs_remove_dir(kobj); 303 sysfs_remove_dir(kobj);
306 unlink(kobj); 304 unlink(kobj);
307} 305}
@@ -314,6 +312,7 @@ void kobject_del(struct kobject * kobj)
314void kobject_unregister(struct kobject * kobj) 312void kobject_unregister(struct kobject * kobj)
315{ 313{
316 pr_debug("kobject %s: unregistering\n",kobject_name(kobj)); 314 pr_debug("kobject %s: unregistering\n",kobject_name(kobj));
315 kobject_hotplug(kobj, KOBJ_REMOVE);
317 kobject_del(kobj); 316 kobject_del(kobj);
318 kobject_put(kobj); 317 kobject_put(kobj);
319} 318}
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 567249bf9331..f6a19d53eaeb 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -248,6 +248,7 @@ int br_sysfs_addif(struct net_bridge_port *p)
248 if (err) 248 if (err)
249 goto out2; 249 goto out2;
250 250
251 kobject_hotplug(&p->kobj, KOBJ_ADD);
251 return 0; 252 return 0;
252 out2: 253 out2:
253 kobject_del(&p->kobj); 254 kobject_del(&p->kobj);
@@ -259,6 +260,7 @@ void br_sysfs_removeif(struct net_bridge_port *p)
259{ 260{
260 pr_debug("br_sysfs_removeif\n"); 261 pr_debug("br_sysfs_removeif\n");
261 sysfs_remove_link(&p->br->ifobj, p->dev->name); 262 sysfs_remove_link(&p->br->ifobj, p->dev->name);
263 kobject_hotplug(&p->kobj, KOBJ_REMOVE);
262 kobject_del(&p->kobj); 264 kobject_del(&p->kobj);
263} 265}
264 266
diff --git a/scripts/ver_linux b/scripts/ver_linux
index bb195a1c0f2d..a28c279c49dd 100755
--- a/scripts/ver_linux
+++ b/scripts/ver_linux
@@ -87,7 +87,7 @@ loadkeys -V 2>&1 | awk \
87 87
88expr --v 2>&1 | awk 'NR==1{print "Sh-utils ", $NF}' 88expr --v 2>&1 | awk 'NR==1{print "Sh-utils ", $NF}'
89 89
90udevinfo -V | awk '{print "udev ", $3}' 90udevinfo -V 2>&1 | grep version | awk '{print "udev ", $3}'
91 91
92if [ -e /proc/modules ]; then 92if [ -e /proc/modules ]; then
93 X=`cat /proc/modules | sed -e "s/ .*$//"` 93 X=`cat /proc/modules | sed -e "s/ .*$//"`